/**
 * 프론트엔드 엑셀 다운로드 유틸리티
 *
 * xlsx 라이브러리를 사용하여 브라우저에서 직접 엑셀 파일을 생성합니다.
 * 모든 리스트 화면에서 공통으로 사용할 수 있습니다.
 *
 * 사용 예시:
 * ```tsx
 * import { downloadExcel } from '@/lib/utils/excel-download';
 *
 * const columns = [
 *   { header: '품목코드', key: 'itemCode' },
 *   { header: '품목명', key: 'itemName' },
 * ];
 *
 * downloadExcel({
 *   data: items,
 *   columns,
 *   filename: '품목목록',
 *   sheetName: '품목',
 * });
 * ```
 */

import { getTodayString } from '@/lib/utils/date';
import { generateExportFilename } from '@/lib/utils/export';

// xlsx는 ~400KB로 무거워서, 실제 사용 시점에 동적 로드
async function loadXLSX() {
  const XLSX = await import('xlsx');
  return XLSX;
}

/**
 * 엑셀 컬럼 정의
 */
export interface ExcelColumn<T = Record<string, unknown>> {
  /** 엑셀 헤더에 표시될 이름 */
  header: string;
  /** 데이터 객체에서 가져올 키 */
  key: keyof T | string;
  /** 값 변환 함수 (선택) */
  transform?: (value: unknown, row: T) => string | number | boolean | null;
  /** 컬럼 너비 (문자 수 기준, 기본값: 15) */
  width?: number;
}

/**
 * 엑셀 다운로드 옵션
 */
export interface ExcelDownloadOptions<T = Record<string, unknown>> {
  /** 다운로드할 데이터 배열 */
  data: T[];
  /** 컬럼 정의 */
  columns: ExcelColumn<T>[];
  /** 파일명 (확장자 제외, 기본값: 'export') */
  filename?: string;
  /** 시트명 (기본값: 'Sheet1') */
  sheetName?: string;
  /** 파일명에 날짜 추가 여부 (기본값: true) */
  appendDate?: boolean;
}

/**
 * 중첩 객체에서 값 추출 (예: 'vendor.name' → vendor 객체의 name 값)
 */
function getNestedValue(obj: Record<string, unknown>, path: string): unknown {
  return path.split('.').reduce((current: unknown, key: string) => {
    if (current && typeof current === 'object' && key in current) {
      return (current as Record<string, unknown>)[key];
    }
    return undefined;
  }, obj);
}

/**
 * 날짜 형식의 파일명 생성
 * export.ts의 generateExportFilename에 위임
 */
function generateFilename(baseName: string, appendDate: boolean): string {
  if (!appendDate) {
    return `${baseName}.xlsx`;
  }
  return generateExportFilename(baseName, 'xlsx');
}

/**
 * 데이터를 엑셀 파일로 다운로드
 */
export async function downloadExcel<T extends Record<string, unknown>>({
  data,
  columns,
  filename = 'export',
  sheetName = 'Sheet1',
  appendDate = true,
}: ExcelDownloadOptions<T>): Promise<void> {
  if (!data || data.length === 0) {
    console.warn('[Excel] 다운로드할 데이터가 없습니다.');
    return;
  }

  try {
    const XLSX = await loadXLSX();

    // 1. 헤더 행 생성
    const headers = columns.map((col) => col.header);

    // 2. 데이터 행 생성
    const rows = data.map((item) => {
      return columns.map((col) => {
        // 값 추출 (중첩 객체 지원)
        const rawValue = getNestedValue(item as Record<string, unknown>, col.key as string);

        // 변환 함수가 있으면 적용
        if (col.transform) {
          return col.transform(rawValue, item);
        }

        // 기본 값 처리
        if (rawValue === null || rawValue === undefined) {
          return '';
        }

        // boolean 처리
        if (typeof rawValue === 'boolean') {
          return rawValue ? 'Y' : 'N';
        }

        // 배열 처리
        if (Array.isArray(rawValue)) {
          return rawValue.join(', ');
        }

        // 객체 처리 (JSON 문자열로)
        if (typeof rawValue === 'object') {
          return JSON.stringify(rawValue);
        }

        return rawValue;
      });
    });

    // 3. 워크시트 생성
    const wsData = [headers, ...rows];
    const ws = XLSX.utils.aoa_to_sheet(wsData);

    // 4. 컬럼 너비 설정
    const colWidths = columns.map((col) => ({
      wch: col.width || Math.max(15, col.header.length * 2),
    }));
    ws['!cols'] = colWidths;

    // 5. 워크북 생성
    const wb = XLSX.utils.book_new();
    XLSX.utils.book_append_sheet(wb, ws, sheetName);

    // 6. 파일 다운로드
    const finalFilename = generateFilename(filename, appendDate);
    XLSX.writeFile(wb, finalFilename);

  } catch (error) {
    console.error('[Excel] 다운로드 실패:', error);
    throw new Error('엑셀 파일 생성에 실패했습니다.');
  }
}

/**
 * 선택된 항목만 엑셀로 다운로드
 */
export async function downloadSelectedExcel<T extends Record<string, unknown>>({
  data,
  selectedIds,
  idField = 'id',
  ...options
}: ExcelDownloadOptions<T> & {
  selectedIds: string[];
  idField?: keyof T | string;
}): Promise<void> {
  const selectedData = data.filter((item) => {
    const id = getNestedValue(item as Record<string, unknown>, idField as string);
    return selectedIds.includes(String(id));
  });

  if (selectedData.length === 0) {
    console.warn('[Excel] 선택된 항목이 없습니다.');
    return;
  }

  await downloadExcel({
    ...options,
    data: selectedData,
  });
}

// ==========================================
// 엑셀 템플릿(양식) 다운로드
// ==========================================

/**
 * 템플릿 컬럼 정의 (업로드용)
 */
export interface TemplateColumn {
  /** 엑셀 헤더에 표시될 이름 */
  header: string;
  /** 데이터 키 (업로드 시 매핑용) */
  key: string;
  /** 필수 여부 */
  required?: boolean;
  /** 데이터 타입 설명 */
  type?: 'text' | 'number' | 'date' | 'boolean' | 'select';
  /** 선택 옵션 (type이 'select'일 때) */
  options?: string[];
  /** 안내 문구 (예: "YYYY-MM-DD 형식") */
  description?: string;
  /** 샘플 값 */
  sampleValue?: string | number | boolean;
  /** 컬럼 너비 */
  width?: number;
}

/**
 * 템플릿 다운로드 옵션
 */
export interface TemplateDownloadOptions {
  /** 컬럼 정의 */
  columns: TemplateColumn[];
  /** 파일명 (확장자 제외) */
  filename?: string;
  /** 시트명 */
  sheetName?: string;
  /** 샘플 데이터 행 포함 여부 (기본값: true) */
  includeSampleRow?: boolean;
  /** 안내 행 포함 여부 (기본값: true) */
  includeGuideRow?: boolean;
}

/**
 * 업로드용 엑셀 템플릿(양식) 다운로드
 *
 * 사용 예시:
 * ```tsx
 * downloadExcelTemplate({
 *   columns: [
 *     { header: '품목코드', key: 'itemCode', required: true, type: 'text', sampleValue: 'KD-FG-001' },
 *     { header: '품목명', key: 'itemName', required: true, type: 'text', sampleValue: '스크린도어' },
 *     { header: '단위', key: 'unit', type: 'select', options: ['EA', 'SET', 'KG'], sampleValue: 'EA' },
 *   ],
 *   filename: '품목등록_양식',
 * });
 * ```
 */
export async function downloadExcelTemplate({
  columns,
  filename = '업로드_양식',
  sheetName = 'Sheet1',
  includeSampleRow = true,
  includeGuideRow = true,
}: TemplateDownloadOptions): Promise<void> {
  try {
    const XLSX = await loadXLSX();
    const wsData: (string | number | boolean)[][] = [];

    // 1. 헤더 행 (필수 표시 포함)
    const headers = columns.map((col) => {
      return col.required ? `${col.header} *` : col.header;
    });
    wsData.push(headers);

    // 2. 안내 행 (데이터 타입, 옵션 등)
    if (includeGuideRow) {
      const guideRow = columns.map((col) => {
        const parts: string[] = [];

        // 타입 표시
        if (col.type === 'select' && col.options) {
          parts.push(`[${col.options.join('/')}]`);
        } else if (col.type === 'date') {
          parts.push('[YYYY-MM-DD]');
        } else if (col.type === 'number') {
          parts.push('[숫자]');
        } else if (col.type === 'boolean') {
          parts.push('[Y/N]');
        }

        // 추가 설명
        if (col.description) {
          parts.push(col.description);
        }

        return parts.join(' ') || '';
      });
      wsData.push(guideRow);
    }

    // 3. 샘플 데이터 행
    if (includeSampleRow) {
      const sampleRow = columns.map((col) => {
        if (col.sampleValue !== undefined) {
          return col.sampleValue;
        }
        // 기본 샘플 값
        if (col.type === 'select' && col.options?.[0]) {
          return col.options[0];
        }
        if (col.type === 'date') {
          return getTodayString();
        }
        if (col.type === 'number') {
          return 0;
        }
        if (col.type === 'boolean') {
          return 'Y';
        }
        return '';
      });
      wsData.push(sampleRow);
    }

    // 4. 워크시트 생성
    const ws = XLSX.utils.aoa_to_sheet(wsData);

    // 5. 컬럼 너비 설정
    const colWidths = columns.map((col) => ({
      wch: col.width || Math.max(15, (col.header.length + (col.required ? 2 : 0)) * 2),
    }));
    ws['!cols'] = colWidths;

    // 6. 헤더 스타일 (볼드) - xlsx 라이브러리 기본 기능으로는 제한적
    // 추후 xlsx-style 라이브러리로 확장 가능

    // 7. 워크북 생성 및 다운로드
    const wb = XLSX.utils.book_new();
    XLSX.utils.book_append_sheet(wb, ws, sheetName);

    const finalFilename = `${filename}.xlsx`;
    XLSX.writeFile(wb, finalFilename);

  } catch (error) {
    console.error('[Excel] 템플릿 다운로드 실패:', error);
    throw new Error('엑셀 템플릿 생성에 실패했습니다.');
  }
}

// ==========================================
// 엑셀 업로드 (파싱)
// ==========================================

/**
 * 엑셀 파일 파싱 결과
 */
export interface ExcelParseResult<T = Record<string, unknown>> {
  /** 파싱 성공 여부 */
  success: boolean;
  /** 파싱된 데이터 */
  data: T[];
  /** 에러 목록 (행별) */
  errors: Array<{
    row: number;
    column?: string;
    message: string;
  }>;
  /** 전체 행 수 */
  totalRows: number;
  /** 유효한 행 수 */
  validRows: number;
}

/**
 * 엑셀 파일을 파싱하여 데이터 배열로 변환
 *
 * 사용 예시:
 * ```tsx
 * const result = await parseExcelFile<ItemMaster>(file, {
 *   columns: [
 *     { header: '품목코드', key: 'itemCode', required: true },
 *     { header: '품목명', key: 'itemName', required: true },
 *   ],
 *   skipRows: 2, // 헤더 + 안내 행 스킵
 * });
 * ```
 */
export async function parseExcelFile<T = Record<string, unknown>>(
  file: File,
  options: {
    columns: TemplateColumn[];
    /** 스킵할 행 수 (헤더, 안내 행 등) */
    skipRows?: number;
    /** 시트 인덱스 (기본값: 0) */
    sheetIndex?: number;
  }
): Promise<ExcelParseResult<T>> {
  const { columns, skipRows = 1, sheetIndex = 0 } = options;
  const XLSX = await loadXLSX();

  return new Promise((resolve) => {
    const reader = new FileReader();

    reader.onload = (e) => {
      try {
        const data = new Uint8Array(e.target?.result as ArrayBuffer);
        const workbook = XLSX.read(data, { type: 'array' });

        // 시트 선택
        const sheetName = workbook.SheetNames[sheetIndex];
        const worksheet = workbook.Sheets[sheetName];

        // JSON으로 변환
        const jsonData = XLSX.utils.sheet_to_json(worksheet, {
          header: 1, // 배열로 반환
          defval: '', // 빈 셀 기본값
        }) as unknown as unknown[][];

        // 헤더 행에서 컬럼 인덱스 매핑
        const headerRow = jsonData[0] as string[];
        const columnIndexMap = new Map<string, number>();

        columns.forEach((col) => {
          // 필수 표시(*)가 있을 수 있으므로 정규화
          const headerIndex = headerRow.findIndex(
            (h) => h?.toString().replace(' *', '').trim() === col.header
          );
          if (headerIndex !== -1) {
            columnIndexMap.set(col.key, headerIndex);
          }
        });

        // 데이터 행 파싱
        const parsedData: T[] = [];
        const errors: ExcelParseResult['errors'] = [];
        const dataRows = jsonData.slice(skipRows);

        dataRows.forEach((row, rowIndex) => {
          const rowNumber = rowIndex + skipRows + 1;
          const rowData: Record<string, unknown> = {};

          columns.forEach((col) => {
            const colIndex = columnIndexMap.get(col.key);
            if (colIndex === undefined) return;

            const rawValue = (row as unknown[])[colIndex];
            const value = rawValue?.toString().trim() || '';

            // 필수 검사
            if (col.required && !value) {
              errors.push({
                row: rowNumber,
                column: col.header,
                message: `${col.header}은(는) 필수입니다`,
              });
            }

            // 타입 검사
            if (value) {
              if (col.type === 'number' && isNaN(Number(value))) {
                errors.push({
                  row: rowNumber,
                  column: col.header,
                  message: `${col.header}은(는) 숫자여야 합니다`,
                });
              }

              if (col.type === 'select' && col.options && !col.options.includes(value)) {
                errors.push({
                  row: rowNumber,
                  column: col.header,
                  message: `${col.header}은(는) [${col.options.join(', ')}] 중 하나여야 합니다`,
                });
              }
            }

            // 값 변환
            if (col.type === 'number' && value) {
              rowData[col.key] = Number(value);
            } else if (col.type === 'boolean') {
              rowData[col.key] = value.toUpperCase() === 'Y' || value === 'true';
            } else {
              rowData[col.key] = value;
            }
          });

          // 빈 행 스킵
          const hasData = Object.values(rowData).some((v) => v !== '' && v !== undefined);
          if (hasData) {
            parsedData.push(rowData as T);
          }
        });

        resolve({
          success: errors.length === 0,
          data: parsedData,
          errors,
          totalRows: dataRows.length,
          validRows: parsedData.length - errors.filter((e, i, arr) =>
            arr.findIndex((x) => x.row === e.row) === i
          ).length,
        });
      } catch (error) {
        console.error('[Excel] 파싱 실패:', error);
        resolve({
          success: false,
          data: [],
          errors: [{ row: 0, message: '파일 형식이 올바르지 않습니다.' }],
          totalRows: 0,
          validRows: 0,
        });
      }
    };

    reader.onerror = () => {
      resolve({
        success: false,
        data: [],
        errors: [{ row: 0, message: '파일을 읽는데 실패했습니다.' }],
        totalRows: 0,
        validRows: 0,
      });
    };

    reader.readAsArrayBuffer(file);
  });
}
