import { Injectable } from '@nestjs/common';

import {
  CdcIssueMatrixRepository,
  type SubmittedCdcIssueResolve,
} from './cdc-issue-matrix.repository';
import { CdcQueryIssueDto } from './dto/cdc-query-issue.dto';

const SOLVED_STATUS_CODE = 'SOLVED';
const IN_PROGRESS_STATUS_CODE = 'IN_PROGRESS';
const NOT_ADDRESSED_STATUS_CODE = 'NOT_ADDRESSED';

type CdcIssueMatrixStatus = {
  id: number;
  code: string;
  name: string;
};

export type CdcIssueMatrixItem = {
  issueId: number;
  issueResolveId: number;
  meetingSummaryId: number;
  title: string;
  description: string;
  recommendation: string;
  category: {
    id: number;
    name: string;
  };
  status: CdcIssueMatrixStatus;
  workingGroup: {
    id: number;
    name: string;
  };
  primaryAgency: {
    id: number;
    name: string;
    logo: string | null;
  } | null;
  rgcDecision: unknown;
  nextStep: unknown;
  remark: unknown;
  submittedAt: Date;
};

export type PaginatedCdcIssueMatrix = {
  data: CdcIssueMatrixItem[];
  meta: {
    page: number;
    limit: number;
    total: number;
    totalPages: number;
  };
};

export type CdcIssueMatrixSummary = {
  totalIssues: number;
  solved: number;
  inProgress: number;
  notAddressed: number;
  totalPrimaryAgencies: number;
};

@Injectable()
export class CdcIssueMatrixService {
  constructor(
    private readonly cdcIssueMatrixRepository: CdcIssueMatrixRepository,
  ) {}

  async findAll(query: CdcQueryIssueDto): Promise<PaginatedCdcIssueMatrix> {
    const page = query.page ?? 1;
    const limit = query.limit ?? 10;
    const latestResolutions = await this.findLatestResolutions();
    const unresolvedItems = latestResolutions
      .filter(
        (resolution) => this.getStatus(resolution).code !== SOLVED_STATUS_CODE,
      )
      .map((resolution) => this.toItem(resolution));

    const total = unresolvedItems.length;
    const startIndex = (page - 1) * limit;

    return {
      data: unresolvedItems.slice(startIndex, startIndex + limit),
      meta: {
        page,
        limit,
        total,
        totalPages: total === 0 ? 0 : Math.ceil(total / limit),
      },
    };
  }

  async getSummary(): Promise<CdcIssueMatrixSummary> {
    const latestResolutions = await this.findLatestResolutions();
    const primaryAgencyIds = new Set<number>();
    let solved = 0;
    let inProgress = 0;
    let notAddressed = 0;

    for (const resolution of latestResolutions) {
      const statusCode = this.getStatus(resolution).code;
      const primaryAgency = this.getPrimaryAgency(resolution);

      if (statusCode === SOLVED_STATUS_CODE) solved += 1;
      if (statusCode === IN_PROGRESS_STATUS_CODE) inProgress += 1;
      if (statusCode === NOT_ADDRESSED_STATUS_CODE) notAddressed += 1;
      if (primaryAgency) primaryAgencyIds.add(primaryAgency.id);
    }

    return {
      totalIssues: latestResolutions.length,
      solved,
      inProgress,
      notAddressed,
      totalPrimaryAgencies: primaryAgencyIds.size,
    };
  }

  private async findLatestResolutions(): Promise<SubmittedCdcIssueResolve[]> {
    const resolutions =
      await this.cdcIssueMatrixRepository.findSubmittedCdcIssueResolves();
    const newestFirst = [...resolutions].sort(
      (firstResolution, secondResolution) =>
        secondResolution.meetingSummary.updatedAt.getTime() -
          firstResolution.meetingSummary.updatedAt.getTime() ||
        secondResolution.id - firstResolution.id,
    );
    const latestByIssueId = new Map<number, SubmittedCdcIssueResolve>();

    for (const resolution of newestFirst) {
      if (!latestByIssueId.has(resolution.issueId)) {
        latestByIssueId.set(resolution.issueId, resolution);
      }
    }

    return Array.from(latestByIssueId.values());
  }

  private toItem(resolution: SubmittedCdcIssueResolve): CdcIssueMatrixItem {
    return {
      issueId: resolution.issueId,
      issueResolveId: resolution.id,
      meetingSummaryId: resolution.meetingSummaryId,
      title: resolution.issue.title,
      description: resolution.issue.description,
      recommendation: resolution.issue.recommendation,
      category: resolution.issue.category,
      status: this.getStatus(resolution),
      workingGroup: resolution.issue.stakeholder,
      primaryAgency: this.getPrimaryAgency(resolution),
      rgcDecision: resolution.rgcDecision,
      nextStep: resolution.nextStep,
      remark: resolution.remark,
      submittedAt: resolution.meetingSummary.updatedAt,
    };
  }

  private getStatus(
    resolution: SubmittedCdcIssueResolve,
  ): CdcIssueMatrixStatus {
    return resolution.issueStatus ?? resolution.issue.issueStatus;
  }

  private getPrimaryAgency(resolution: SubmittedCdcIssueResolve) {
    const primaryAgency = resolution.issue.governmentAgencies.find(
      (agency) => agency.agencyOrder === 1,
    );

    return primaryAgency?.stakeholder ?? null;
  }
}
