/**
 * 스케줄 자동 생성 서비스
 *
 * 스케줄 미리보기 생성, 적용, 조회 로직을 처리합니다.
 */

import { pool } from "../database/db";

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

export interface ScheduleGenerationConfig {
  scheduleType: "PRODUCTION" | "MAINTENANCE" | "SHIPPING" | "WORK_ASSIGN";
  source: {
    tableName: string;
    groupByField: string;
    quantityField: string;
    dueDateField?: string;
  };
  resource: {
    type: string;
    idField: string;
    nameField: string;
  };
  rules: {
    leadTimeDays?: number;
    dailyCapacity?: number;
    workingDays?: number[];
    considerStock?: boolean;
    stockTableName?: string;
    stockQtyField?: string;
    safetyStockField?: string;
  };
  target: {
    tableName: string;
  };
}

export interface SchedulePreview {
  toCreate: any[];
  toDelete: any[];
  toUpdate: any[];
  summary: {
    createCount: number;
    deleteCount: number;
    updateCount: number;
    totalQty: number;
  };
}

export interface ApplyOptions {
  deleteExisting: boolean;
  updateMode: "replace" | "merge";
}

export interface ApplyResult {
  created: number;
  deleted: number;
  updated: number;
}

export interface ScheduleListQuery {
  scheduleType?: string;
  resourceType?: string;
  resourceId?: string;
  startDate?: string;
  endDate?: string;
  status?: string;
  companyCode: string;
}

// ============================================================================
// 서비스 클래스
// ============================================================================

export class ScheduleService {
  /**
   * 스케줄 미리보기 생성
   */
  async generatePreview(
    config: ScheduleGenerationConfig,
    sourceData: any[],
    period: { start: string; end: string } | undefined,
    companyCode: string
  ): Promise<SchedulePreview> {
    console.log("[ScheduleService] generatePreview 시작:", {
      scheduleType: config.scheduleType,
      sourceDataCount: sourceData.length,
      period,
      companyCode,
    });

    // 기본 기간 설정 (현재 월)
    const now = new Date();
    const defaultPeriod = {
      start: new Date(now.getFullYear(), now.getMonth(), 1)
        .toISOString()
        .split("T")[0],
      end: new Date(now.getFullYear(), now.getMonth() + 1, 0)
        .toISOString()
        .split("T")[0],
    };
    const effectivePeriod = period || defaultPeriod;

    // 1. 소스 데이터를 리소스별로 그룹화
    const groupedData = this.groupByResource(sourceData, config);

    // 2. 각 리소스에 대해 스케줄 생성
    const toCreate: any[] = [];
    let totalQty = 0;

    for (const [resourceId, items] of Object.entries(groupedData)) {
      const schedules = this.generateSchedulesForResource(
        resourceId,
        items as any[],
        config,
        effectivePeriod,
        companyCode
      );
      toCreate.push(...schedules);
      totalQty += schedules.reduce((sum, s) => sum + (s.plan_qty || 0), 0);
    }

    // 3. 기존 스케줄 조회 (삭제 대상)
    // 그룹 키에서 리소스 ID만 추출 ("리소스ID|날짜" 형식에서 "리소스ID"만)
    const resourceIds = [
      ...new Set(Object.keys(groupedData).map((key) => key.split("|")[0])),
    ];
    const toDelete = await this.getExistingSchedules(
      config.scheduleType,
      resourceIds,
      effectivePeriod,
      companyCode
    );

    // 4. 미리보기 결과 생성
    const preview: SchedulePreview = {
      toCreate,
      toDelete,
      toUpdate: [], // 현재는 Replace 모드만 지원
      summary: {
        createCount: toCreate.length,
        deleteCount: toDelete.length,
        updateCount: 0,
        totalQty,
      },
    };

    console.log("[ScheduleService] generatePreview 완료:", preview.summary);

    return preview;
  }

  /**
   * 스케줄 적용
   */
  async applySchedules(
    config: ScheduleGenerationConfig,
    preview: SchedulePreview,
    options: ApplyOptions,
    companyCode: string,
    userId: string
  ): Promise<ApplyResult> {
    console.log("[ScheduleService] applySchedules 시작:", {
      createCount: preview.summary.createCount,
      deleteCount: preview.summary.deleteCount,
      options,
      companyCode,
      userId,
    });

    const client = await pool.connect();
    const result: ApplyResult = { created: 0, deleted: 0, updated: 0 };

    try {
      await client.query("BEGIN");

      // 1. 기존 스케줄 삭제
      if (options.deleteExisting && preview.toDelete.length > 0) {
        const deleteIds = preview.toDelete.map((s) => s.schedule_id);
        await client.query(
          `DELETE FROM schedule_mng 
           WHERE schedule_id = ANY($1) AND company_code = $2`,
          [deleteIds, companyCode]
        );
        result.deleted = deleteIds.length;
        console.log("[ScheduleService] 스케줄 삭제 완료:", result.deleted);
      }

      // 2. 새 스케줄 생성
      for (const schedule of preview.toCreate) {
        await client.query(
          `INSERT INTO schedule_mng (
            company_code, schedule_type, schedule_name,
            resource_type, resource_id, resource_name,
            start_date, end_date, due_date,
            plan_qty, unit, status, priority,
            source_table, source_id, source_group_key,
            auto_generated, generated_at, generated_by,
            metadata, created_by, updated_by
          ) VALUES (
            $1, $2, $3, $4, $5, $6, $7, $8, $9, $10,
            $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22
          )`,
          [
            companyCode,
            schedule.schedule_type,
            schedule.schedule_name,
            schedule.resource_type,
            schedule.resource_id,
            schedule.resource_name,
            schedule.start_date,
            schedule.end_date,
            schedule.due_date || null,
            schedule.plan_qty,
            schedule.unit || null,
            schedule.status || "PLANNED",
            schedule.priority || null,
            schedule.source_table || null,
            schedule.source_id || null,
            schedule.source_group_key || null,
            true,
            new Date(),
            userId,
            schedule.metadata ? JSON.stringify(schedule.metadata) : null,
            userId,
            userId,
          ]
        );
        result.created++;
      }

      await client.query("COMMIT");
      console.log("[ScheduleService] applySchedules 완료:", result);

      return result;
    } catch (error) {
      await client.query("ROLLBACK");
      console.error("[ScheduleService] applySchedules 오류:", error);
      throw error;
    } finally {
      client.release();
    }
  }

  /**
   * 스케줄 목록 조회
   */
  async getScheduleList(
    query: ScheduleListQuery
  ): Promise<{ data: any[]; total: number }> {
    const conditions: string[] = [];
    const params: any[] = [];
    let paramIndex = 1;

    // company_code 필터
    if (query.companyCode !== "*") {
      conditions.push(`company_code = $${paramIndex++}`);
      params.push(query.companyCode);
    }

    // scheduleType 필터
    if (query.scheduleType) {
      conditions.push(`schedule_type = $${paramIndex++}`);
      params.push(query.scheduleType);
    }

    // resourceType 필터
    if (query.resourceType) {
      conditions.push(`resource_type = $${paramIndex++}`);
      params.push(query.resourceType);
    }

    // resourceId 필터
    if (query.resourceId) {
      conditions.push(`resource_id = $${paramIndex++}`);
      params.push(query.resourceId);
    }

    // 기간 필터
    if (query.startDate) {
      conditions.push(`end_date >= $${paramIndex++}`);
      params.push(query.startDate);
    }
    if (query.endDate) {
      conditions.push(`start_date <= $${paramIndex++}`);
      params.push(query.endDate);
    }

    // status 필터
    if (query.status) {
      conditions.push(`status = $${paramIndex++}`);
      params.push(query.status);
    }

    const whereClause =
      conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";

    const result = await pool.query(
      `SELECT * FROM schedule_mng 
       ${whereClause}
       ORDER BY start_date, resource_id`,
      params
    );

    return {
      data: result.rows,
      total: result.rows.length,
    };
  }

  /**
   * 스케줄 삭제
   */
  async deleteSchedule(
    scheduleId: number,
    companyCode: string,
    userId: string
  ): Promise<{ success: boolean; message?: string }> {
    const result = await pool.query(
      `DELETE FROM schedule_mng 
       WHERE schedule_id = $1 AND (company_code = $2 OR $2 = '*')
       RETURNING schedule_id`,
      [scheduleId, companyCode]
    );

    if (result.rowCount === 0) {
      return {
        success: false,
        message: "스케줄을 찾을 수 없거나 권한이 없습니다.",
      };
    }

    // 이력 기록
    await pool.query(
      `INSERT INTO schedule_history (company_code, schedule_id, action, changed_by)
       VALUES ($1, $2, 'DELETE', $3)`,
      [companyCode, scheduleId, userId]
    );

    return { success: true };
  }

  // ============================================================================
  // 헬퍼 메서드
  // ============================================================================

  /**
   * 소스 데이터를 리소스별로 그룹화
   * - 기준일(dueDateField)이 설정된 경우: 리소스 + 기준일 조합으로 그룹화
   * - 기준일이 없는 경우: 리소스별로만 그룹화
   */
  private groupByResource(
    sourceData: any[],
    config: ScheduleGenerationConfig
  ): Record<string, any[]> {
    const grouped: Record<string, any[]> = {};
    const dueDateField = config.source.dueDateField;

    for (const item of sourceData) {
      const resourceId = item[config.resource.idField];
      if (!resourceId) continue;

      // 그룹 키 생성: 기준일이 있으면 "리소스ID|기준일", 없으면 "리소스ID"
      let groupKey = resourceId;
      if (dueDateField && item[dueDateField]) {
        // 날짜를 YYYY-MM-DD 형식으로 정규화
        const dueDate = new Date(item[dueDateField])
          .toISOString()
          .split("T")[0];
        groupKey = `${resourceId}|${dueDate}`;
      }

      if (!grouped[groupKey]) {
        grouped[groupKey] = [];
      }
      grouped[groupKey].push(item);
    }

    console.log("[ScheduleService] 그룹화 결과:", {
      groupCount: Object.keys(grouped).length,
      groups: Object.keys(grouped),
      dueDateField,
    });

    return grouped;
  }

  /**
   * 리소스에 대한 스케줄 생성
   * - groupKey 형식: "리소스ID" 또는 "리소스ID|기준일(YYYY-MM-DD)"
   */
  private generateSchedulesForResource(
    groupKey: string,
    items: any[],
    config: ScheduleGenerationConfig,
    period: { start: string; end: string },
    companyCode: string
  ): any[] {
    const schedules: any[] = [];

    // 그룹 키에서 리소스ID와 기준일 분리
    const [resourceId, groupDueDate] = groupKey.split("|");
    const resourceName = items[0]?.[config.resource.nameField] || resourceId;

    // 총 수량 계산
    const totalQty = items.reduce((sum, item) => {
      return sum + (parseFloat(item[config.source.quantityField]) || 0);
    }, 0);

    if (totalQty <= 0) return schedules;

    // 스케줄 규칙 적용
    const {
      leadTimeDays = 3,
      dailyCapacity = totalQty,
      workingDays = [1, 2, 3, 4, 5],
    } = config.rules;

    // 기준일(납기일/마감일) 결정
    let dueDate: Date;
    if (groupDueDate) {
      // 그룹 키에 기준일이 포함된 경우
      dueDate = new Date(groupDueDate);
    } else if (config.source.dueDateField) {
      // 아이템에서 기준일 찾기 (가장 빠른 날짜)
      let earliestDate: Date | null = null;
      for (const item of items) {
        const itemDueDate = item[config.source.dueDateField];
        if (itemDueDate) {
          const date = new Date(itemDueDate);
          if (!earliestDate || date < earliestDate) {
            earliestDate = date;
          }
        }
      }
      dueDate = earliestDate || new Date(period.end);
    } else {
      // 기준일이 없으면 기간 종료일 사용
      dueDate = new Date(period.end);
    }

    // 종료일 = 기준일 (납기일에 맞춰 완료)
    const endDate = new Date(dueDate);

    // 시작일 계산 (종료일에서 리드타임만큼 역산)
    const startDate = new Date(endDate);
    startDate.setDate(startDate.getDate() - leadTimeDays);

    // 스케줄명 생성 (기준일 포함)
    const dueDateStr = dueDate.toISOString().split("T")[0];
    const scheduleName = groupDueDate
      ? `${resourceName} (${dueDateStr})`
      : `${resourceName} - ${config.scheduleType}`;

    // 스케줄 생성
    schedules.push({
      schedule_type: config.scheduleType,
      schedule_name: scheduleName,
      resource_type: config.resource.type,
      resource_id: resourceId,
      resource_name: resourceName,
      start_date: startDate.toISOString(),
      end_date: endDate.toISOString(),
      due_date: dueDate.toISOString(),
      plan_qty: totalQty,
      status: "PLANNED",
      source_table: config.source.tableName,
      source_id: items
        .map((i) => i.id || i.order_no || i.sales_order_no)
        .join(","),
      source_group_key: resourceId,
      metadata: {
        sourceCount: items.length,
        dailyCapacity,
        leadTimeDays,
        workingDays,
        groupDueDate: groupDueDate || null,
      },
    });

    console.log("[ScheduleService] 스케줄 생성:", {
      groupKey,
      resourceId,
      resourceName,
      dueDate: dueDateStr,
      totalQty,
      startDate: startDate.toISOString().split("T")[0],
      endDate: endDate.toISOString().split("T")[0],
    });

    return schedules;
  }

  /**
   * 기존 스케줄 조회 (삭제 대상)
   */
  private async getExistingSchedules(
    scheduleType: string,
    resourceIds: string[],
    period: { start: string; end: string },
    companyCode: string
  ): Promise<any[]> {
    if (resourceIds.length === 0) return [];

    const result = await pool.query(
      `SELECT * FROM schedule_mng 
       WHERE schedule_type = $1 
         AND resource_id = ANY($2)
         AND end_date >= $3
         AND start_date <= $4
         AND (company_code = $5 OR $5 = '*')
         AND auto_generated = true`,
      [scheduleType, resourceIds, period.start, period.end, companyCode]
    );

    return result.rows;
  }
}
