import { api as client } from "@/auth/http";
import { URL_CONSTANTS } from "@/constants/URLS";
import type {
  RuleEngineListMeta,
  RuleEngineListResult,
  RuleEngineRecord,
  RuleEngineResourceSlug,
} from "@/types/rule-engine";
import { unwrap } from "@/utils/endpoint";

export interface RuleEngineListParams {
  search?: string;
  page?: number;
  pageSize?: number;
  isActive?: boolean;
  status?: string;
  sortBy?: string;
  sortOrder?: "ASC" | "DESC";
  requiresDirectorApproval?: boolean;
}

export interface RuleEngineReorderPayload {
  ids: string[];
  requiresDirectorApproval?: boolean;
}

const RESOURCE_BASE: Record<RuleEngineResourceSlug, string> = {
  "cargo-types": URL_CONSTANTS.RULE_ENGINE.CARGO_TYPES,
  "container-types": URL_CONSTANTS.RULE_ENGINE.CONTAINER_TYPES,
  "wagon-types": URL_CONSTANTS.RULE_ENGINE.WAGON_TYPES,
  "priority-configs": URL_CONSTANTS.RULE_ENGINE.PRIORITY_CONFIGS,
  "service-types": URL_CONSTANTS.RULE_ENGINE.SERVICE_TYPES,
  "weight-limit-rules": URL_CONSTANTS.RULE_ENGINE.WEIGHT_LIMIT_RULES,
  yards: URL_CONSTANTS.RULE_ENGINE.YARDS,
  "shipping-lines": URL_CONSTANTS.RULE_ENGINE.SHIPPING_LINES,
  rates: URL_CONSTANTS.RULE_ENGINE.RATES,
  "approval-rules": URL_CONSTANTS.RULE_ENGINE.APPROVAL_RULES,
};

const byIdPath = (resource: RuleEngineResourceSlug, id: string): string => {
  switch (resource) {
    case "cargo-types":
      return URL_CONSTANTS.RULE_ENGINE.CARGO_TYPE_BY_ID(id);
    case "container-types":
      return URL_CONSTANTS.RULE_ENGINE.CONTAINER_TYPE_BY_ID(id);
    case "wagon-types":
      return URL_CONSTANTS.RULE_ENGINE.WAGON_TYPE_BY_ID(id);
    case "priority-configs":
      return URL_CONSTANTS.RULE_ENGINE.PRIORITY_CONFIG_BY_ID(id);
    case "service-types":
      return URL_CONSTANTS.RULE_ENGINE.SERVICE_TYPE_BY_ID(id);
    case "weight-limit-rules":
      return URL_CONSTANTS.RULE_ENGINE.WEIGHT_LIMIT_RULE_BY_ID(id);
    case "yards":
      return URL_CONSTANTS.RULE_ENGINE.YARD_BY_ID(id);
    case "shipping-lines":
      return URL_CONSTANTS.RULE_ENGINE.SHIPPING_LINE_BY_ID(id);
    case "rates":
      return URL_CONSTANTS.RULE_ENGINE.RATE_BY_ID(id);
    case "approval-rules":
      return URL_CONSTANTS.RULE_ENGINE.APPROVAL_RULE_BY_ID(id);
    default:
      return `${RESOURCE_BASE[resource]}/${id}`;
  }
};

const defaultMeta = (
  dataLength: number,
  page = 1,
  pageSize = 10,
): RuleEngineListMeta => ({
  total: dataLength,
  page,
  pageSize,
  totalPages: Math.max(1, Math.ceil(dataLength / pageSize)),
});

const isPaginatedListResult = <T extends RuleEngineRecord>(
  value: unknown,
): value is RuleEngineListResult<T> =>
  Boolean(value) &&
  typeof value === "object" &&
  "data" in (value ?? {}) &&
  Array.isArray((value as RuleEngineListResult<T>).data);

const normalizeList = <T extends RuleEngineRecord>(
  payload: unknown,
  page = 1,
  pageSize = 10,
): RuleEngineListResult<T> => {
  if (isPaginatedListResult<T>(payload)) {
    return {
      data: payload.data,
      meta: payload.meta ?? defaultMeta(payload.data.length, page, pageSize),
    };
  }

  const body = unwrap(payload as { data: unknown }) as unknown;

  if (isPaginatedListResult<T>(body)) {
    return {
      data: body.data,
      meta: body.meta ?? defaultMeta(body.data.length, page, pageSize),
    };
  }

  if (Array.isArray(body)) {
    return {
      data: body as T[],
      meta: defaultMeta(body.length, page, pageSize),
    };
  }

  return { data: [], meta: defaultMeta(0, page, pageSize) };
};

const normalizeEntity = <T extends RuleEngineRecord>(payload: unknown): T => {
  return unwrap(payload as { data: T }) as T;
};

export const ruleEngineService = {
  list: async <T extends RuleEngineRecord>(
    resource: RuleEngineResourceSlug,
    params?: RuleEngineListParams,
  ): Promise<RuleEngineListResult<T>> => {
    const page = params?.page ?? 1;
    const pageSize = params?.pageSize ?? 10;
    const response = await client.get(RESOURCE_BASE[resource], {
      params: {
        page,
        pageSize,
        search: params?.search,
        isActive: params?.isActive,
        status: params?.status,
        sortBy: params?.sortBy,
        sortOrder: params?.sortOrder,
        requiresDirectorApproval: params?.requiresDirectorApproval,
      },
    });
    return normalizeList<T>(response.data, page, pageSize);
  },

  getById: async <T extends RuleEngineRecord>(
    resource: RuleEngineResourceSlug,
    id: string,
  ): Promise<T> => {
    const response = await client.get(byIdPath(resource, id));
    return normalizeEntity<T>(response.data);
  },

  create: async <T extends RuleEngineRecord>(
    resource: RuleEngineResourceSlug,
    payload: Record<string, unknown>,
  ): Promise<T> => {
    const response = await client.post(RESOURCE_BASE[resource], payload);
    return normalizeEntity<T>(response.data);
  },

  update: async <T extends RuleEngineRecord>(
    resource: RuleEngineResourceSlug,
    id: string,
    payload: Record<string, unknown>,
  ): Promise<T> => {
    const response = await client.patch(byIdPath(resource, id), payload);
    return normalizeEntity<T>(response.data);
  },

  remove: async (
    resource: RuleEngineResourceSlug,
    id: string,
  ): Promise<void> => {
    await client.delete(byIdPath(resource, id));
  },

  reorder: async (
    resource: RuleEngineResourceSlug,
    payload: RuleEngineReorderPayload,
  ): Promise<void> => {
    await client.post(`${RESOURCE_BASE[resource]}/reorder`, payload);
  },

  moveOrder: async (
    resource: RuleEngineResourceSlug,
    id: string,
    direction: "up" | "down",
  ): Promise<void> => {
    await client.post(`${byIdPath(resource, id)}/move-order`, { direction });
  },

  submitRate: async <T extends RuleEngineRecord>(id: string): Promise<T> => {
    const response = await client.post(
      URL_CONSTANTS.RULE_ENGINE.RATE_SUBMIT(id),
    );
    return normalizeEntity<T>(response.data);
  },

  approveRate: async <T extends RuleEngineRecord>(id: string): Promise<T> => {
    const response = await client.post(
      URL_CONSTANTS.RULE_ENGINE.RATE_APPROVE(id),
    );
    return normalizeEntity<T>(response.data);
  },

  getApprovalChain: async (
    requiresDirectorApproval = true,
  ): Promise<RuleEngineRecord[]> => {
    const response = await client.get(
      URL_CONSTANTS.RULE_ENGINE.APPROVAL_RULES_CHAIN,
      {
        params: { requiresDirectorApproval },
      },
    );
    const body = unwrap(response.data) as unknown;
    if (Array.isArray(body)) return body as RuleEngineRecord[];
    if (
      body &&
      typeof body === "object" &&
      "data" in body &&
      Array.isArray((body as { data: unknown }).data)
    ) {
      return (body as { data: RuleEngineRecord[] }).data;
    }
    return [];
  },
};
