import {
  ADMIN_SUCCESS,
  DUPLICATE_UPLOAD_FILE_NAME_FOUND,
  FAILED_UPLOAD,
  NOT_FOUND_RECOVERY_DATA,
  NOT_FOUND_REQUIRED_PARAMETER,
  UPLOAD_DATA_NOT_FOUND,
} from '@api/constants/admin.constant';
import { NOT_FOUND_FILE } from '@api/constants/api.constant';
import { formatDuplicateErrorMsg } from '@api/helpers/excelErrorFormat';
import { findDuplicate } from '@api/helpers/findDuplicate';
import { mysqlErrorWrapper } from '@lib/drizzle/src/drizzle.utill';
import { ExcelService } from '@lib/excel/src';
import { BadRequestException, Injectable } from '@nestjs/common';
import { CellValue, Workbook } from 'exceljs';
import { Response } from 'express';
import {
  CreateCompareBasicDto,
  DeleteCompareBasicDto,
  GetCompareBasicListDto,
  UpdateCompareBasicDto,
} from './compare-basic.dto';
import { CompareBasicRepository } from './compare-basic.repository';

@Injectable()
export class CompareBasicService {
  constructor(
    private compareBasicRepository: CompareBasicRepository,
    private excelService: ExcelService,
  ) {}

  async getCompareBasicList(dto: GetCompareBasicListDto) {
    const where = await this.compareBasicRepository.whereClause(dto);
    const orderBy = await this.compareBasicRepository.orderByClause(dto);
    const list = await this.compareBasicRepository.getList(
      where,
      orderBy,
      dto.page,
      false,
    );
    const count = await this.compareBasicRepository.count(where);

    return {
      count,
      list,
    };
  }

  /**  목록 엑셀다운로드 */
  async downloadCompareBasicList(dto: GetCompareBasicListDto, res: Response) {
    const where = await this.compareBasicRepository.whereClause(dto);
    const orderBy = await this.compareBasicRepository.orderByClause(dto);
    const dataGenerator: any = await this.compareBasicRepository.getList(
      where,
      orderBy,
      dto.page,
      true,
    );
    const columns = this.buildExcelColumns();
    const title =
      dto.cpbType === 'COM001'
        ? '중대질병'
        : dto.cpbType === 'COM005'
          ? '주의사항'
          : dto.cpbType === 'COM006'
            ? '숨은보험(진단)'
            : dto.cpbType === 'COM007'
              ? '숨은보험(치아)'
              : dto.cpbType === 'COM010'
                ? '치아(틀니)'
                : dto.cpbType === 'COM011'
                  ? '치아(1년)'
                  : dto.cpbType === 'COM012'
                    ? '치아(5년)'
                    : '목록';
    await this.excelService.generate(res, title, { dataGenerator, columns });
  }

  /**양식 다운로드 */
  async downloadUploadForm(res: Response) {
    const dataGenerator = [
      {
        cpbDiseaseCode: 'EX)TEST01',
        cpbName: '결핍바이러스병',
        cpbViewName: '바이러스병',
        cpbIsSicked: '포함',
      },
      {
        cpbDiseaseCode: 'EX)TEST02',
        cpbName: '면역결핍바이러스병',
        cpbViewName: '면역바이러스병',
        cpbIsSicked: '미포함',
      },
    ];
    const columns = this.buildUploadFormExcelColumns();
    await this.excelService.generateUploadForm(res, { dataGenerator, columns });
  }

  /** 기본데이터 업로드 */
  async uploadCompareBasic(cpbType: string, file: Express.Multer.File) {
    if (!file) throw new BadRequestException(NOT_FOUND_FILE);
    const uploadData = await this.extractDataFromExcel(cpbType, file.buffer);
    await this.excelValidate(uploadData);
    const result = await this.dataUpload(cpbType, uploadData);
    return result;
  }

  async getDetail(cpbId: number) {
    return await this.compareBasicRepository.getDetail(cpbId);
  }

  async createCompareBasic(dto: CreateCompareBasicDto) {
    return await this.compareBasicRepository.create(dto).catch((error) => {
      mysqlErrorWrapper(error);
    });
  }

  async updateCompareBasic(cpbId: number, dto: UpdateCompareBasicDto) {
    return await this.compareBasicRepository
      .update(cpbId, dto)
      .catch((error) => {
        mysqlErrorWrapper(error);
      });
  }

  async recoveryCompareBasic(cpbType: string) {
    //* 임시데이터 존재여부 확인
    const count =
      await this.compareBasicRepository.doexExistCompareType(cpbType);
    if (count === 0) throw new BadRequestException(NOT_FOUND_RECOVERY_DATA);
    return await this.compareBasicRepository
      .recovery(cpbType)
      .catch((error) => {
        mysqlErrorWrapper(error);
      });
  }

  async deleteCompareBasic(dto: DeleteCompareBasicDto) {
    return await this.compareBasicRepository.delete(dto.ids);
  }

  //*--------------------------------------------- excel ---------------------------------------------//
  /** 엑셀 다운로드 컬럼 */
  buildExcelColumns(): { header: string; key: string }[] {
    return [
      { header: '진단코드', key: 'cpbDiseaseCode' },
      { header: '진단명', key: 'cpbName' },
      { header: '진단노출명', key: 'cpbViewName' },
      { header: '유병자유무', key: 'cpbIsSicked' },
      { header: '등록일', key: 'cpbCreatedAt' },
    ];
  }

  /** 양식 컬럼 */
  buildUploadFormExcelColumns(): { header: string; key: string }[] {
    return [
      { header: '진단코드(*필수)', key: 'cpbDiseaseCode' },
      { header: '진단명(*필수)', key: 'cpbName' },
      { header: '진단노출명', key: 'cpbViewName' },
      { header: '유병자포함여부(*필수)', key: 'cpbIsSicked' },
    ];
  }

  /** 엑셀 데이터 추출
   * Readable.from(buffer)로 읽어올 시 한글 깨짐현상 발생
   */
  async extractDataFromExcel(type: string, buffer: Buffer) {
    const workbook = new Workbook();
    const a = await workbook.xlsx.load(buffer);
    const sheet = a.getWorksheet(1);
    const UploadCompareBasicData: UploadCompareBasicRow[] = [];
    sheet?.eachRow((row, i) => {
      const [cpbDiseaseCode, cpbName, cpbViewName, cpbIsSicked] = (
        row.values as Array<CellValue>
      ).slice(1);
      if (!cpbDiseaseCode || !cpbName || !cpbIsSicked)
        throw new BadRequestException(NOT_FOUND_REQUIRED_PARAMETER);
      if (i !== 1) {
        UploadCompareBasicData.push({
          cpbType: type,
          cpbDiseaseCode: cpbDiseaseCode.toString().replace(/\s/g, ''),
          cpbName: cpbName.toString().trim(),
          cpbViewName: cpbViewName ? cpbViewName.toString().trim() : null,
          cpbIsSicked: cpbIsSicked === '포함' ? 1 : 0,
        });
      }
    });
    console.log('UploadCompareBasicData:: ', UploadCompareBasicData);
    // const excelStream = Readable.from(buffer);
    // const workbookReader = new stream.xlsx.WorkbookReader(excelStream, {});
    // const UploadCompareDetailData: UploadCompareBasicRow[] = [];

    // for await (const worksheetReader of workbookReader) {
    //   for await (const row of worksheetReader) {
    //     if (row.number === 1) continue;
    //     const [cpbDiseaseCode, cpbName, cpbViewName, cpbIsSicked] = (
    //       row.values as Array<CellValue>
    //     )
    //       .slice(1)
    //       .join()
    //       .split(',');
    //     if (!cpbDiseaseCode || !cpbName || !cpbIsSicked)
    //       throw new BadRequestException(NOT_FOUND_REQUIRED_PARAMETER);
    //     if (!cpbDiseaseCode && !cpbName && !cpbViewName) continue;
    //     UploadCompareDetailData.push({
    //       cpbType: type,
    //       cpbDiseaseCode: cpbDiseaseCode.replace(/\s/g, ''),
    //       cpbName: cpbName.trim(),
    //       cpbViewName: cpbViewName ? cpbViewName.trim() : null,
    //       cpbIsSicked: cpbIsSicked === '포함' ? 1 : 0,
    //     });
    //   }
    // }

    return UploadCompareBasicData;
  }

  /**  업로드 데이터 유효성 검사 */
  async excelValidate(uploadData: UploadCompareBasicRow[]) {
    this.doesExistInExcel(uploadData).duplicate(uploadData);
    return this;
  }

  private doesExistInExcel(uploadData: UploadCompareBasicRow[]) {
    if (uploadData.length === 0) {
      throw new BadRequestException(UPLOAD_DATA_NOT_FOUND);
    }
    return this;
  }

  private duplicate(uploadData: UploadCompareBasicRow[]) {
    const diseaseCodes = uploadData.map((data) => data.cpbDiseaseCode);
    const duplicates = findDuplicate(diseaseCodes);
    if (duplicates.length > 0) {
      if (duplicates.length > 3) {
        const sliceDuplicates = duplicates.slice(0, 3);

        throw new BadRequestException(
          formatDuplicateErrorMsg(
            sliceDuplicates,
            duplicates.length,
            DUPLICATE_UPLOAD_FILE_NAME_FOUND,
          ),
        );
      } else {
        throw new BadRequestException(
          formatDuplicateErrorMsg(
            duplicates,
            0,
            DUPLICATE_UPLOAD_FILE_NAME_FOUND,
          ),
        );
      }
    }
    return this;
  }

  /** 데이터 업로드  */
  async dataUpload(
    cpdType: string,
    data: UploadCompareBasicRow[],
  ): Promise<{ code: string; message: string }> {
    await this.compareBasicRepository.upload(cpdType, data).catch((error) => {
      throw new BadRequestException(FAILED_UPLOAD);
    });
    return ADMIN_SUCCESS;
  }
}

/** 엑셀 업로드 인터페이스 */
export interface UploadCompareBasicRow {
  cpbType: string;
  cpbDiseaseCode: string;
  cpbName: string;
  cpbViewName: string | null;
  cpbIsSicked: 0 | 1;
}
