import { Injectable } from '@nestjs/common';
import { AssemblyMonitoringRepository } from './assembly-monitoring.repository';
import {
  AssemblyMonitoringBaseQueryDto,
  AssemblyMonitoringDailyQueryDto,
  AssemblyMonitoringProcessesQueryDto,
  AssemblyMonitoringQueryWithFactUnit,
  AssemblyMonitoringRealtimeQueryDto,
  AssemblyMonitoringUnitDetailQueryDto,
  AssemblyMonitoringUnitDetailQueryWithDefaults,
  AssemblyMonitoringUnitListQueryDto,
  AssemblyMonitoringUnitListQueryWithDefaults,
} from './dto/request';
import {
  AssemblyMonitoringOverviewResponseDto,
  AssemblyMonitoringUnitDetailResponseDto,
  AssemblyMonitoringUnitListResponseDto,
} from './dto/response';
import {
  transformProcColumns,
  transformProcessStatus,
} from 'src/api/monitoring/cp/assembly/transformers/proc.transformer';
import { AssemblyMonitoringProcRowsByProcessDto } from './dto/internal/proc.transform.dto';
import { buildUnitDetailTracks } from 'src/api/monitoring/cp/assembly/transformers/unit-detail.transformer';
import { convertGubunToWaitingStatus } from 'src/api/monitoring/cp/assembly/constants/process-status.const';
import { PinoLogger } from 'nestjs-pino';
import { AssemblyMonitoringInactiveWorkerDbDto } from './dto/internal/inactive-worker.db.dto';

@Injectable()
export class AssemblyMonitoringService {
  private readonly defaultFactUnit = 2;

  constructor(
    private readonly repository: AssemblyMonitoringRepository,
    private readonly logger: PinoLogger,
  ) {
    this.logger.setContext(AssemblyMonitoringService.name);
  }

  /**
   * 실시간 모니터링 데이터 조회
   * PROC_MAP을 사용하여 가변 PROC_* 컬럼을 고정된 processName 배열로 변환
   * 모든 row를 하나로 합쳐서 공정별로 그룹핑
   */
  async findRealtimeData(
    query: AssemblyMonitoringRealtimeQueryDto,
  ): Promise<AssemblyMonitoringOverviewResponseDto> {
    const normalizedQuery = this.applyDefaults(query);
    const rawData = await this.repository.findRealtimeData(normalizedQuery);

    return this.buildResponse(
      rawData.rows,
      'cp_monitoring_realtime',
      transformProcColumns,
      rawData.inactiveWorkers,
    );
  }

  /**
   * 일일 모니터링 데이터 조회
   */
  async findDailyData(
    query: AssemblyMonitoringDailyQueryDto,
  ): Promise<AssemblyMonitoringOverviewResponseDto> {
    const normalizedQuery = this.applyDefaults(query);
    const rawData = await this.repository.findDailyData(normalizedQuery);

    return this.buildResponse(rawData, 'cp_monitoring_daily');
  }

  /**
   * Processes - 공정 상태 조회
   */
  async findProcesses(
    query: AssemblyMonitoringProcessesQueryDto,
  ): Promise<AssemblyMonitoringOverviewResponseDto> {
    const normalizedQuery = this.applyDefaults(query);
    const rawData = await this.repository.findProcesses(normalizedQuery);

    return this.buildResponse(
      rawData,
      'cp_process_status',
      transformProcessStatus,
    );
  }

  /**
   * Processes - 대기/제공 공정 상태 조회
   */
  async findWaitingProcesses(
    query: AssemblyMonitoringProcessesQueryDto,
  ): Promise<AssemblyMonitoringOverviewResponseDto> {
    const normalizedQuery = this.applyDefaults(query);
    const rawData = await this.repository.findWaitingProcesses(normalizedQuery);

    return this.buildResponse(rawData, 'cp_process_waiting', (rows) =>
      transformProcessStatus(rows, convertGubunToWaitingStatus),
    );
  }

  /**
   * Units - 호기 목록 조회
   */
  async findUnitList(
    query: AssemblyMonitoringUnitListQueryDto,
  ): Promise<AssemblyMonitoringUnitListResponseDto[]> {
    const normalizedQuery = this.applyUnitListDefaults(query);
    return this.repository.findUnitList(normalizedQuery);
  }

  /**
   * Units - 특정 호기 공정 상세 조회
   */
  async findUnitDetail(
    query: AssemblyMonitoringUnitDetailQueryDto,
  ): Promise<AssemblyMonitoringUnitDetailResponseDto | null> {
    const normalizedQuery = this.applyUnitDefaults(query);
    const rows = await this.repository.findUnitDetail(normalizedQuery);
    const tracks = buildUnitDetailTracks(rows);

    if (tracks.length === 0) {
      return null;
    }

    if (tracks.length > 1) {
      this.logger.warn(
        {
          event: 'svc.monitoring.multiple_tracks',
          operation: 'cp_unit_detail',
          prodPlanSeq: query.prodPlanSeq,
          serialNo: query.serialNo,
          count: tracks.length,
        },
        'multiple tracks returned',
      );
    }

    return tracks[0];
  }

  private buildResponse(
    rawData: any[],
    operation: string,
    transformer: (
      rows: any[],
    ) => AssemblyMonitoringProcRowsByProcessDto = transformProcColumns,
    inactiveWorkers: AssemblyMonitoringInactiveWorkerDbDto[] = [],
  ): AssemblyMonitoringOverviewResponseDto {
    this.logger.debug(
      {
        event: 'svc.monitoring.process_rows',
        operation,
        rowCount: rawData.length,
      },
      'processing raw rows',
    );

    const processes = transformer(rawData);

    const totalCount = Object.values(processes).reduce(
      (sum, tasks) => sum + tasks.length,
      0,
    );

    this.logger.debug(
      {
        event: 'svc.monitoring.merge_rows',
        operation,
        totalCount,
      },
      'merged rows',
    );

    return {
      processes,
      inactiveWorkers: this.mapInactiveWorkers(inactiveWorkers),
    };
  }

  private mapInactiveWorkers(
    rows: AssemblyMonitoringInactiveWorkerDbDto[],
  ): AssemblyMonitoringOverviewResponseDto['inactiveWorkers'] {
    return rows.map((row) => {
      const empSeq = Number(row.EmpSeq);
      return {
        employeeId: Number.isFinite(empSeq) ? empSeq : 0,
        employeeName: row.EmpName ?? '',
        departmentName: row.DeptName ?? '',
      };
    });
  }

  private applyDefaults<T extends AssemblyMonitoringBaseQueryDto>(
    query: T,
  ): AssemblyMonitoringQueryWithFactUnit<T> {
    return {
      ...query,
      factUnit: query.factUnit ?? this.defaultFactUnit,
    };
  }

  private applyUnitDefaults(
    query: AssemblyMonitoringUnitDetailQueryDto,
  ): AssemblyMonitoringUnitDetailQueryWithDefaults {
    return {
      ...this.applyDefaults(query),
      specSeq: query.specSeq ?? 0,
      workingEndDate: query.workingEndDate ?? '',
    };
  }

  private applyUnitListDefaults(
    query: AssemblyMonitoringUnitListQueryDto,
  ): AssemblyMonitoringUnitListQueryWithDefaults {
    const prodPlanMonth = this.resolveProdPlanMonth(query);
    return {
      ...this.applyDefaults(query),
      prodPlanMonth,
      modelSeq: query.modelSeq ?? 0,
      serialNo: query.serialNo ?? '',
    };
  }

  private resolveProdPlanMonth(
    query: AssemblyMonitoringUnitListQueryDto,
  ): string {
    const trimmedMonth =
      typeof query.prodPlanMonth === 'string' ? query.prodPlanMonth.trim() : '';
    if (trimmedMonth) return trimmedMonth;

    const now = new Date();
    const year = String(now.getFullYear());
    const month = String(now.getMonth() + 1).padStart(2, '0');
    return `${year}${month}`;
  }
}
