/**
 * executePopAction - POP 액션 실행 순수 함수
 *
 * pop-button, pop-string-list 등 여러 컴포넌트에서 재사용 가능한
 * 액션 실행 코어 로직. React 훅에 의존하지 않음.
 *
 * 사용처:
 *   - usePopAction 훅 (pop-button용 래퍼)
 *   - pop-string-list 카드 버튼 (직접 호출)
 *   - 향후 pop-table 행 액션 등
 */

import type { ButtonMainAction, ButtonTask } from "@/lib/registry/pop-components/pop-button";
import { apiClient } from "@/lib/api/client";
import { dataApi } from "@/lib/api/data";

// ========================================
// 타입 정의
// ========================================

/** 액션 실행 결과 */
export interface ActionResult {
  success: boolean;
  data?: unknown;
  error?: string;
}

/** 이벤트 발행 함수 시그니처 (usePopEvent의 publish와 동일) */
type PublishFn = (eventName: string, payload?: unknown) => void;

/** executePopAction 옵션 */
interface ExecuteOptions {
  /** 필드 매핑 (소스 컬럼명 → 타겟 컬럼명) */
  fieldMapping?: Record<string, string>;
  /** 화면 ID (이벤트 발행 시 사용) */
  screenId?: string;
  /** 이벤트 발행 함수 (순수 함수이므로 외부에서 주입) */
  publish?: PublishFn;
}

// ========================================
// 내부 헬퍼
// ========================================

/**
 * 필드 매핑 적용
 * 소스 데이터의 컬럼명을 타겟 테이블 컬럼명으로 변환
 */
function applyFieldMapping(
  rowData: Record<string, unknown>,
  mapping?: Record<string, string>
): Record<string, unknown> {
  if (!mapping || Object.keys(mapping).length === 0) {
    return { ...rowData };
  }

  const result: Record<string, unknown> = {};
  for (const [sourceKey, value] of Object.entries(rowData)) {
    // 매핑이 있으면 타겟 키로 변환, 없으면 원본 키 유지
    const targetKey = mapping[sourceKey] || sourceKey;
    result[targetKey] = value;
  }
  return result;
}

/**
 * rowData에서 PK 추출
 * id > pk 순서로 시도, 없으면 rowData 전체를 복합키로 사용
 */
function extractPrimaryKey(
  rowData: Record<string, unknown>
): string | number | Record<string, unknown> {
  if (rowData.id != null) return rowData.id as string | number;
  if (rowData.pk != null) return rowData.pk as string | number;
  // 복합키: rowData 전체를 Record로 전달 (dataApi.deleteRecord가 object 지원)
  return rowData as Record<string, unknown>;
}

// ========================================
// 메인 함수
// ========================================

/**
 * POP 액션 실행 (순수 함수)
 *
 * @param action - 버튼 메인 액션 설정
 * @param rowData - 대상 행 데이터 (리스트 컴포넌트에서 전달)
 * @param options - 필드 매핑, screenId, publish 함수
 * @returns 실행 결과
 */
export async function executePopAction(
  action: ButtonMainAction,
  rowData?: Record<string, unknown>,
  options?: ExecuteOptions
): Promise<ActionResult> {
  const { fieldMapping, publish } = options || {};

  try {
    switch (action.type) {
      // ── 저장 ──
      case "save": {
        if (!action.targetTable) {
          return { success: false, error: "저장 대상 테이블이 설정되지 않았습니다." };
        }
        const data = rowData
          ? applyFieldMapping(rowData, fieldMapping)
          : {};
        const result = await dataApi.createRecord(action.targetTable, data);
        return { success: !!result?.success, data: result?.data, error: result?.message };
      }

      // ── 삭제 ──
      case "delete": {
        if (!action.targetTable) {
          return { success: false, error: "삭제 대상 테이블이 설정되지 않았습니다." };
        }
        if (!rowData) {
          return { success: false, error: "삭제할 데이터가 없습니다." };
        }
        const mappedData = applyFieldMapping(rowData, fieldMapping);
        const pk = extractPrimaryKey(mappedData);
        const result = await dataApi.deleteRecord(action.targetTable, pk);
        return { success: !!result?.success, error: result?.message };
      }

      // ── API 호출 ──
      case "api": {
        if (!action.apiEndpoint) {
          return { success: false, error: "API 엔드포인트가 설정되지 않았습니다." };
        }
        const body = rowData
          ? applyFieldMapping(rowData, fieldMapping)
          : undefined;
        const method = (action.apiMethod || "POST").toUpperCase();

        let response;
        switch (method) {
          case "GET":
            response = await apiClient.get(action.apiEndpoint, { params: body });
            break;
          case "POST":
            response = await apiClient.post(action.apiEndpoint, body);
            break;
          case "PUT":
            response = await apiClient.put(action.apiEndpoint, body);
            break;
          case "DELETE":
            response = await apiClient.delete(action.apiEndpoint, { data: body });
            break;
          default:
            response = await apiClient.post(action.apiEndpoint, body);
        }

        const resData = response?.data;
        return {
          success: resData?.success !== false,
          data: resData?.data ?? resData,
        };
      }

      // ── 모달 열기 ──
      case "modal": {
        if (!publish) {
          return { success: false, error: "이벤트 발행 함수가 제공되지 않았습니다." };
        }
        publish("__pop_modal_open__", {
          modalId: action.modalScreenId,
          title: action.modalTitle,
          mode: action.modalMode,
          items: action.modalItems,
          rowData,
        });
        return { success: true };
      }

      // ── 이벤트 발행 ──
      case "event": {
        if (!publish) {
          return { success: false, error: "이벤트 발행 함수가 제공되지 않았습니다." };
        }
        if (!action.eventName) {
          return { success: false, error: "이벤트 이름이 설정되지 않았습니다." };
        }
        publish(action.eventName, {
          ...(action.eventPayload || {}),
          row: rowData,
        });
        return { success: true };
      }

      default:
        return { success: false, error: `알 수 없는 액션 타입: ${action.type}` };
    }
  } catch (err: unknown) {
    const message =
      err instanceof Error ? err.message : "알 수 없는 오류가 발생했습니다.";
    return { success: false, error: message };
  }
}

// ========================================
// v2: 작업 목록 실행
// ========================================

/** 수집된 데이터 구조 */
export interface CollectedPayload {
  items?: Record<string, unknown>[];
  fieldValues?: Record<string, unknown>;
  mappings?: {
    cardList?: Record<string, unknown> | null;
    field?: Record<string, unknown> | null;
  };
  cartChanges?: {
    toCreate?: Record<string, unknown>[];
    toUpdate?: Record<string, unknown>[];
    toDelete?: (string | number)[];
  };
}

/** 작업 목록 실행 옵션 */
interface ExecuteTaskListOptions {
  publish: PublishFn;
  componentId: string;
  collectedData?: CollectedPayload;
}

/**
 * 작업 목록을 순차 실행한다.
 * 데이터 관련 작업(data-save, data-update, data-delete, cart-save)은
 * 하나의 API 호출로 묶어 백엔드에서 트랜잭션 처리한다.
 * 나머지 작업(modal-open, navigate 등)은 프론트엔드에서 직접 처리한다.
 */
export async function executeTaskList(
  tasks: ButtonTask[],
  options: ExecuteTaskListOptions,
): Promise<ActionResult> {
  const { publish, componentId, collectedData } = options;

  // 데이터 작업과 프론트 전용 작업 분리
  const DATA_TASK_TYPES = new Set(["data-save", "data-update", "data-delete", "cart-save"]);
  const dataTasks = tasks.filter((t) => DATA_TASK_TYPES.has(t.type));
  const frontTasks = tasks.filter((t) => !DATA_TASK_TYPES.has(t.type));

  let backendData: Record<string, unknown> | null = null;

  try {
    // 1. 데이터 작업이 있으면 백엔드에 일괄 전송
    if (dataTasks.length > 0) {
      const result = await apiClient.post("/pop/execute-action", {
        tasks: dataTasks,
        data: {
          items: collectedData?.items ?? [],
          fieldValues: collectedData?.fieldValues ?? {},
        },
        mappings: collectedData?.mappings ?? {},
        cartChanges: collectedData?.cartChanges,
      });

      if (!result.data?.success) {
        return {
          success: false,
          error: result.data?.message || "데이터 작업 실행에 실패했습니다.",
          data: result.data,
        };
      }
      backendData = result.data;
    }

    const innerData = (backendData as Record<string, unknown>)?.data as Record<string, unknown> | undefined;
    const generatedCodes = innerData?.generatedCodes as
      Array<{ targetColumn: string; code: string; showResultModal?: boolean }> | undefined;
    const hasResultModal = generatedCodes?.some((g) => g.showResultModal);

    // 2. 프론트엔드 전용 작업 순차 실행 (채번 모달이 있으면 navigate 보류)
    const deferredNavigateTasks: ButtonTask[] = [];
    for (const task of frontTasks) {
      switch (task.type) {
        case "modal-open":
          publish("__pop_modal_open__", {
            modalId: task.modalScreenId,
            title: task.modalTitle,
            mode: task.modalMode,
            items: task.modalItems,
          });
          break;

        case "navigate":
          if (hasResultModal) {
            deferredNavigateTasks.push(task);
          } else if (task.targetScreenId) {
            publish("__pop_navigate__", { screenId: task.targetScreenId, params: task.params });
          }
          break;

        case "close-modal":
          publish("__pop_close_modal__");
          break;

        case "refresh":
          if (!hasResultModal) {
            publish("__pop_refresh__");
          }
          break;

        case "api-call": {
          if (!task.apiEndpoint) break;
          const method = (task.apiMethod || "POST").toUpperCase();
          switch (method) {
            case "GET":
              await apiClient.get(task.apiEndpoint);
              break;
            case "PUT":
              await apiClient.put(task.apiEndpoint);
              break;
            case "DELETE":
              await apiClient.delete(task.apiEndpoint);
              break;
            default:
              await apiClient.post(task.apiEndpoint);
          }
          break;
        }

        case "custom-event":
          if (task.flowId) {
            await apiClient.post(`/dataflow/node-flows/${task.flowId}/execute`, {});
          } else if (task.eventName) {
            publish(task.eventName, task.eventPayload ?? {});
          }
          break;
      }
    }

    // 3. 완료 이벤트
    if (!hasResultModal) {
      publish(`__comp_output__${componentId}__action_completed`, {
        action: "task-list",
        success: true,
      });
    }

    return {
      success: true,
      data: {
        generatedCodes,
        deferredTasks: deferredNavigateTasks,
        ...(backendData ?? {}),
      },
    };
  } catch (err: unknown) {
    const message = err instanceof Error ? err.message : "작업 실행 중 오류가 발생했습니다.";
    return { success: false, error: message };
  }
}
