/**
 * useDataSource - POP 컴포넌트용 데이터 CRUD 통합 훅
 *
 * DataSourceConfig를 받아서 자동으로 적절한 API를 선택하여 데이터를 조회/생성/수정/삭제한다.
 *
 * 조회 분기:
 * - aggregation 또는 joins가 있으면 → SQL 빌더 + executeQuery (대시보드와 동일)
 * - 그 외 → dataApi.getTableData (단순 테이블 조회)
 *
 * CRUD:
 * - save: dataApi.createRecord
 * - update: dataApi.updateRecord
 * - remove: dataApi.deleteRecord
 *
 * 사용 패턴:
 * ```typescript
 * // 집계 조회 (대시보드용)
 * const { data, loading } = useDataSource({
 *   tableName: "sales_order",
 *   aggregation: { type: "sum", column: "amount", groupBy: ["category"] },
 *   refreshInterval: 30,
 * });
 *
 * // 단순 목록 조회 (테이블용)
 * const { data, refetch } = useDataSource({
 *   tableName: "purchase_order",
 *   sort: [{ column: "created_at", direction: "desc" }],
 *   limit: 20,
 * });
 *
 * // 저장/삭제 (버튼용)
 * const { save, remove } = useDataSource({ tableName: "inbound_record" });
 * await save({ supplier_id: "SUP-001", quantity: 50 });
 * ```
 */

import { useState, useCallback, useEffect, useRef } from "react";
import { apiClient } from "@/lib/api/client";
import { dashboardApi } from "@/lib/api/dashboard";
import { dataApi } from "@/lib/api/data";
import type { DataSourceConfig, DataSourceFilter } from "@/lib/registry/pop-components/types";
import { validateDataSourceConfig, buildAggregationSQL } from "./popSqlBuilder";

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

/** 조회 결과 */
export interface DataSourceResult {
  /** 데이터 행 배열 */
  rows: Record<string, unknown>[];
  /** 단일 집계 값 (aggregation 시) 또는 전체 행 수 */
  value: number;
  /** 전체 행 수 (페이징용) */
  total: number;
}

/** CRUD 작업 결과 */
export interface MutationResult {
  success: boolean;
  data?: unknown;
  error?: string;
}

/** refetch 시 전달할 오버라이드 필터 */
interface OverrideOptions {
  filters?: Record<string, unknown>;
}

// ===== 내부: 집계/조인 조회 =====

/**
 * 집계 또는 조인이 포함된 DataSourceConfig를 SQL로 변환하여 실행
 * dataFetcher.ts의 fetchAggregatedData와 동일한 로직
 */
async function fetchWithSqlBuilder(
  config: DataSourceConfig
): Promise<DataSourceResult> {
  const sql = buildAggregationSQL(config);

  // API 호출: apiClient(axios) 우선, dashboardApi(fetch) 폴백
  let queryResult: { columns: string[]; rows: Record<string, unknown>[] };
  try {
    // 1차: apiClient (axios 기반, 인증/세션 안정적)
    const response = await apiClient.post("/dashboards/execute-query", { query: sql });
    if (response.data?.success && response.data?.data) {
      queryResult = response.data.data;
    } else {
      throw new Error(response.data?.message || "쿼리 실행 실패");
    }
  } catch {
    // 2차: dashboardApi (fetch 기반, 폴백)
    queryResult = await dashboardApi.executeQuery(sql);
  }

  if (queryResult.rows.length === 0) {
    return { rows: [], value: 0, total: 0 };
  }

  // PostgreSQL bigint/numeric는 JS에서 문자열로 반환됨 → 숫자 변환
  const processedRows = queryResult.rows.map((row) => {
    const converted: Record<string, unknown> = { ...row };
    for (const key of Object.keys(converted)) {
      const val = converted[key];
      if (typeof val === "string" && val !== "" && !isNaN(Number(val))) {
        converted[key] = Number(val);
      }
    }
    return converted;
  });

  // 첫 번째 행의 value 컬럼 추출
  const firstRow = processedRows[0];
  const numericValue = parseFloat(
    String(firstRow.value ?? firstRow[queryResult.columns[0]] ?? 0)
  );

  return {
    rows: processedRows,
    value: Number.isFinite(numericValue) ? numericValue : 0,
    total: processedRows.length,
  };
}

// ===== 내부: 단순 테이블 조회 =====

/**
 * aggregation/joins 없는 단순 테이블 조회
 * dataApi.getTableData 래핑
 */
async function fetchSimpleTable(
  config: DataSourceConfig,
  overrideFilters?: Record<string, unknown>
): Promise<DataSourceResult> {
  // config.filters를 Record<string, unknown> 형태로 변환
  const baseFilters: Record<string, unknown> = {};
  if (config.filters?.length) {
    for (const f of config.filters) {
      if (f.column?.trim()) {
        baseFilters[f.column] = f.value;
      }
    }
  }

  // overrideFilters가 있으면 병합 (같은 키는 override가 덮어씀)
  const mergedFilters = overrideFilters
    ? { ...baseFilters, ...overrideFilters }
    : baseFilters;

  const tableResult = await dataApi.getTableData(config.tableName, {
    page: 1,
    size: config.limit ?? 100,
    sortBy: config.sort?.[0]?.column,
    sortOrder: config.sort?.[0]?.direction,
    filters: Object.keys(mergedFilters).length > 0 ? mergedFilters : undefined,
  });

  return {
    rows: tableResult.data,
    value: tableResult.total ?? tableResult.data.length,
    total: tableResult.total ?? tableResult.data.length,
  };
}

// ===== 내부: overrideFilters를 DataSourceFilter 배열에 병합 =====

/**
 * 기존 config에 overrideFilters를 병합한 새 config 생성
 * 같은 column이 있으면 override 값으로 대체
 */
function mergeFilters(
  config: DataSourceConfig,
  overrideFilters?: Record<string, unknown>
): DataSourceConfig {
  if (!overrideFilters || Object.keys(overrideFilters).length === 0) {
    return config;
  }

  // 기존 filters에서 override 대상이 아닌 것만 유지
  const overrideColumns = new Set(Object.keys(overrideFilters));
  const existingFilters: DataSourceFilter[] = (config.filters ?? []).filter(
    (f) => !overrideColumns.has(f.column)
  );

  // override를 DataSourceFilter로 변환하여 추가
  const newFilters: DataSourceFilter[] = Object.entries(overrideFilters).map(
    ([column, value]) => ({
      column,
      operator: "=" as const,
      value,
    })
  );

  return {
    ...config,
    filters: [...existingFilters, ...newFilters],
  };
}

// ===== 메인 훅 =====

/**
 * POP 컴포넌트용 데이터 CRUD 통합 훅
 *
 * @param config - DataSourceConfig (tableName 필수)
 * @returns data, loading, error, refetch, save, update, remove
 */
export function useDataSource(config: DataSourceConfig) {
  const [data, setData] = useState<DataSourceResult>({
    rows: [],
    value: 0,
    total: 0,
  });
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);

  // config를 ref로 저장 (콜백 안정성)
  const configRef = useRef(config);
  configRef.current = config;

  // 자동 새로고침 타이머
  const refreshTimerRef = useRef<ReturnType<typeof setInterval> | null>(null);

  // ===== 조회 (READ) =====

  const refetch = useCallback(
    async (options?: OverrideOptions): Promise<void> => {
      const currentConfig = configRef.current;

      // 테이블명 없으면 조회하지 않음
      if (!currentConfig.tableName?.trim()) {
        return;
      }

      setLoading(true);
      setError(null);

      try {
        const hasAggregation = !!currentConfig.aggregation;
        const hasJoins = !!(currentConfig.joins && currentConfig.joins.length > 0);

        let result: DataSourceResult;

        if (hasAggregation || hasJoins) {
          // 집계/조인 → SQL 빌더 경로
          // 설정 완료 여부 검증
          const merged = mergeFilters(currentConfig, options?.filters);
          const validationError = validateDataSourceConfig(merged);
          if (validationError) {
            setError(validationError);
            setLoading(false);
            return;
          }
          result = await fetchWithSqlBuilder(merged);
        } else {
          // 단순 조회 → dataApi 경로
          result = await fetchSimpleTable(currentConfig, options?.filters);
        }

        setData(result);
      } catch (err: unknown) {
        const message = err instanceof Error ? err.message : "데이터 조회 실패";
        setError(message);
      } finally {
        setLoading(false);
      }
    },
    [] // configRef 사용으로 의존성 불필요
  );

  // ===== 생성 (CREATE) =====

  const save = useCallback(
    async (record: Record<string, unknown>): Promise<MutationResult> => {
      const tableName = configRef.current.tableName;
      if (!tableName?.trim()) {
        return { success: false, error: "테이블이 설정되지 않았습니다" };
      }

      try {
        const result = await dataApi.createRecord(tableName, record);
        return {
          success: result.success ?? true,
          data: result.data,
          error: result.message,
        };
      } catch (err: unknown) {
        const message = err instanceof Error ? err.message : "레코드 생성 실패";
        return { success: false, error: message };
      }
    },
    []
  );

  // ===== 수정 (UPDATE) =====

  const update = useCallback(
    async (
      id: string | number,
      record: Record<string, unknown>
    ): Promise<MutationResult> => {
      const tableName = configRef.current.tableName;
      if (!tableName?.trim()) {
        return { success: false, error: "테이블이 설정되지 않았습니다" };
      }

      try {
        const result = await dataApi.updateRecord(tableName, id, record);
        return {
          success: result.success ?? true,
          data: result.data,
          error: result.message,
        };
      } catch (err: unknown) {
        const message = err instanceof Error ? err.message : "레코드 수정 실패";
        return { success: false, error: message };
      }
    },
    []
  );

  // ===== 삭제 (DELETE) =====

  const remove = useCallback(
    async (
      id: string | number | Record<string, unknown>
    ): Promise<MutationResult> => {
      const tableName = configRef.current.tableName;
      if (!tableName?.trim()) {
        return { success: false, error: "테이블이 설정되지 않았습니다" };
      }

      try {
        const result = await dataApi.deleteRecord(tableName, id);
        return {
          success: result.success ?? true,
          data: result.data,
          error: result.message,
        };
      } catch (err: unknown) {
        const message = err instanceof Error ? err.message : "레코드 삭제 실패";
        return { success: false, error: message };
      }
    },
    []
  );

  // ===== 자동 조회 + 새로고침 =====

  // config.tableName 또는 refreshInterval이 변경되면 재조회
  const tableName = config.tableName;
  const refreshInterval = config.refreshInterval;

  useEffect(() => {
    // 테이블명 있으면 초기 조회
    if (tableName?.trim()) {
      refetch();
    }

    // refreshInterval 설정 시 자동 새로고침
    if (refreshInterval && refreshInterval > 0) {
      const sec = Math.max(5, refreshInterval); // 최소 5초
      refreshTimerRef.current = setInterval(() => {
        refetch();
      }, sec * 1000);
    }

    return () => {
      if (refreshTimerRef.current) {
        clearInterval(refreshTimerRef.current);
        refreshTimerRef.current = null;
      }
    };
  }, [tableName, refreshInterval, refetch]);

  return {
    data,
    loading,
    error,
    refetch,
    save,
    update,
    remove,
  } as const;
}
