import { NOT_FOUND_REQUIRED_PARAMETER } from '@api/constants/api.constant';
import { BadRequestException } from '@nestjs/common';
import dayjs from 'dayjs';
import { and, eq, gte, isNotNull, notLike, or } from 'drizzle-orm';
import {
    tnAnalysisBasic,
    tnAnalysisCarBasic,
    tnAnalysisCarDetail,
    tnAnalysisDetail,
    tnB2cAnalysisBasic,
    tnB2cAnalysisDetail,
} from 'drizzle/schema';
import {
    GetAnalysisBasicListDto,
    GetAnalysisCarBasicListDto,
    GetAnalysisCarDetailListDto,
    GetAnalysisDetailListDto,
    GetAnalysisPrescribeListDto,
} from './analyze.dto';

interface AnalyzeWhereBuilderParams {
  oddId?: number;
  asbId?: number;
  dto?:
    | GetAnalysisBasicListDto
    | GetAnalysisDetailListDto
    | GetAnalysisPrescribeListDto;
  oddDate?: string;
  asbTreatStartDate?: string;
}

interface CarAnalzeWhereBuilderParams {
  oddId?: number;
  acbId?: number;
  dto?: GetAnalysisCarBasicListDto | GetAnalysisCarDetailListDto;
  oddDate?: string;
  acbTreatStartDate?: string;
}

export class AnalyzeWhereBuilder {
  private where: any[] = [];
  constructor(private params: AnalyzeWhereBuilderParams) {}

  searchId() {
    const { oddId, asbId } = this.params;
    if (oddId !== undefined) {
      this.where.push(eq(tnAnalysisBasic.oddId, oddId));
    }
    if (asbId !== undefined) {
      this.where.push(eq(tnAnalysisDetail.asbId, asbId));
    }
    return this;
  }

  searchDate() {
    const { oddDate: date, asbTreatStartDate } = this.params;
    if (asbTreatStartDate !== undefined) {
      const treatDate = eq(
        tnAnalysisBasic.asbTreatStartDate,
        asbTreatStartDate,
      );
      this.where.push(treatDate);
    }

    return this;
  }

  ansType(listType: 'basic' | 'detail') {
    if (listType === 'basic') {
      this.basicAnsType();
    } else {
      this.detailAnsType();
    }

    return this;
  }

  build() {
    return and(...this.where);
  }

  //------------------------- private -------------------------//
  // "약국"이 포함된 병원명을 제외하는 조건 추가
  private notPharmacy() {
    this.where.push(notLike(tnAnalysisBasic.asbHospitalName, '%약국%'));
    return this;
  }

  private basicAnsType() {
    const { dto, oddDate: date } = this.params;
    // if (!dto || !date) {
    //   throw new Error('필수 파라미터 누락 (dto, date)');
    // }
    const basicDto = dto as GetAnalysisBasicListDto;
    const { ansType, asbDiseaseCode, searchYear } = basicDto;
    const oddDate = dayjs(date);

    switch (ansType) {
      case 'ANS001': {
        // 3개월이내(내원, 수술, 검사, 투약, 기타)/유병자는 투약만 제외
        const subDate = oddDate.subtract(3, 'month').format('YYYY-MM-DD');
        const searchDate = gte(tnAnalysisBasic.asbTreatStartDate, subDate);
        this.where.push(searchDate);
        break;
      }
      case 'ANS002': {
        // 1년이내 검사
        this.ans002();
        this.notPharmacy();
        const subDate = oddDate.subtract(365, 'day').format('YYYY-MM-DD');
        const searchDate = gte(tnAnalysisBasic.asbTreatStartDate, subDate);
        this.where.push(searchDate);
        break;
      }
      case 'ANS003': {
        // 5년이내 7일이상 치료 / 30일이상 투약일시엔 해당 basic이 상세가 됨
        if (!basicDto.asbDiseaseCode)
          throw new BadRequestException(NOT_FOUND_REQUIRED_PARAMETER);
        this.notPharmacy();
        const eqColumn = eq(tnAnalysisBasic.asbDiseaseCode, asbDiseaseCode!);
        const treatType = eq(tnAnalysisBasic.asbTreatType, '외래');
        this.where.push(eqColumn, treatType);
        break;
      }
      case 'ANS004': {
        // 5년이내 입원/수술 (유병자의 경우 2년이내, 5년이내)
        if (!basicDto.searchYear)
          throw new BadRequestException(NOT_FOUND_REQUIRED_PARAMETER);
        // ANS004 공통 조건
        this.notPharmacy();
        const inNotNullColum = or(
          eq(tnAnalysisBasic.asbTreatType, '입원'),
          isNotNull(tnAnalysisDetail.asdOperation),
        );
        this.where.push(inNotNullColum);
        if (searchYear === 2) {
          const subDate = oddDate.subtract(730, 'day').format('YYYY-MM-DD');
          const searchDate = gte(tnAnalysisBasic.asbTreatStartDate, subDate);
          this.where.push(searchDate);
        }
        break;
      }
      case 'ANS005': {
        // 5년이내 11(6)대 질병 진단, 수술
        // ANS003과 조건이 동일하지만 조건이 변경될 수 있으니 따로 구성함
        if (!basicDto.asbDiseaseCode)
          throw new BadRequestException(NOT_FOUND_REQUIRED_PARAMETER);
        this.notPharmacy();
        const eqColumn = eq(tnAnalysisBasic.asbDiseaseCode, asbDiseaseCode!);
        this.where.push(eqColumn);
        break;
      }
      case 'ANS006': {
        // 숨은보험(진단), 약국포함, 기본진단
        if (!basicDto.asbDiseaseCode)
          throw new BadRequestException(NOT_FOUND_REQUIRED_PARAMETER);
        const eqColumn = eq(tnAnalysisBasic.asbDiseaseCode, asbDiseaseCode!);
        const inNotNullColumn = isNotNull(tnAnalysisBasic.asbInDisease);
        this.where.push(eqColumn, inNotNullColumn);
        break;
      }
      case 'ANS007': {
        // 숨은보험(치아) 기본진단 and 상세치료
        this.ans007();
        this.notPharmacy();
        const eqColumn = eq(tnAnalysisBasic.asbInTooth, 1);
        this.where.push(eqColumn);
        break;
      }
      case 'ANS008': {
        // 숨은보험(수술) 상세치료
        this.ans008();
        this.notPharmacy();
        break;
      }
      case 'ANS009': {
        // 숨은보험(기타) 상세치료
        if (basicDto.asbDiseaseCode) {
          this.where.push(eq(tnAnalysisBasic.asbDiseaseCode, asbDiseaseCode!));
        }
        this.ans009();
        this.notPharmacy();
        break;
      }
      case 'ANS010': {
        // 치아고지(틀니) 기본진단 or 상세치료
        this.ans010();
        this.notPharmacy();
        break;
      }
      case 'ANS011': {
        // 치아고지(1년) 기본진단 and 상세치료
        this.ans011();
        this.notPharmacy();
        const subDate = oddDate.subtract(365, 'day').format('YYYY-MM-DD');
        const searchDate = gte(tnAnalysisBasic.asbTreatStartDate, subDate);
        const inNotNullColumn = isNotNull(tnAnalysisBasic.asbToothOne);
        this.where.push(searchDate, inNotNullColumn);
        break;
      }
      case 'ANS012': {
        // 치아고지(5년) 기본진단 and 상세치료
        this.ans012();
        this.notPharmacy();
        const inNotNullColumn = isNotNull(tnAnalysisBasic.asbToothFive);
        this.where.push(inNotNullColumn);
        break;
      }
      case 'ANS013': {
        // 주의사항
        if (!basicDto.asbDiseaseCode)
          throw new BadRequestException(NOT_FOUND_REQUIRED_PARAMETER);
        this.notPharmacy();
        const eqColumn = eq(tnAnalysisBasic.asbDiseaseCode, asbDiseaseCode!);
        const inNotNullColumn = isNotNull(tnAnalysisBasic.asbNotice);
        this.where.push(eqColumn, inNotNullColumn);
        break;
      }
    }

    return and(...this.where);
  }

  private detailAnsType() {
    const { dto } = this.params;
    if (!dto) {
      throw new Error('필수 파라미터 누락 (dto)');
    }
    const detailDto = dto as GetAnalysisDetailListDto;
    const { ansType } = detailDto;

    switch (ansType) {
      case 'ANS001': {
        // 수술, 검사, 투약만
        // 투약은 다른 수술/검사가 있을 경우 투약항목에 포함 됨
        const inNotNullColumn = or(
          isNotNull(tnAnalysisDetail.asdOperation),
          isNotNull(tnAnalysisDetail.asdExamination),
        );
        this.where.push(inNotNullColumn);
        break;
      }
      case 'ANS002': {
        // 1년이내 검사
        this.ans002();
        break;
      }
      case 'ANS004': {
        // 5년이내 입원/수술
        const inNotNullColumn = isNotNull(tnAnalysisDetail.asdOperation);
        this.where.push(inNotNullColumn);
        break;
      }
      case 'ANS007': {
        // 숨은보험(치아)
        this.ans007();
        break;
      }
      case 'ANS008': {
        // 숨은보험(수술)
        this.ans008();
        break;
      }
      case 'ANS009': {
        // 숨은보험(기타)
        this.ans009();
        break;
      }
      case 'ANS010': {
        // 치아고지(틀니)
        this.ans010();
        break;
      }
      case 'ANS011': {
        // 치아고지(1년)
        this.ans011();
        break;
      }
      case 'ANS012': {
        // 치아고지(5년)
        this.ans012();
        break;
      }
    }

    return and(...this.where);
  }

  //------------------------- ansType별 공통 조건 -------------------------//
  // ANS002: 1년이내 검사
  private ans002() {
    this.where.push(isNotNull(tnAnalysisDetail.asdExamination));
    return this;
  }

  // ANS007: 숨은보험(치아)
  private ans007() {
    this.where.push(isNotNull(tnAnalysisDetail.asdInTooth));
    return this;
  }

  // ANS008: 숨은보험(수술)
  private ans008() {
    this.where.push(isNotNull(tnAnalysisDetail.asdInOperation));
    return this;
  }

  // ANS009: 숨은보험(기타)
  private ans009() {
    this.where.push(isNotNull(tnAnalysisDetail.asdInEtc));
    return this;
  }

  // ANS010: 치아고지(틀니)
  private ans010() {
    this.where.push(isNotNull(tnAnalysisBasic.asbDentures));
    return this;
  }

  // ANS011: 치아고지 (1년)
  private ans011() {
    this.where.push(isNotNull(tnAnalysisDetail.asdToothOne));
    return this;
  }

  // ANS012: 치아고지 (5년)
  private ans012() {
    this.where.push(isNotNull(tnAnalysisDetail.asdToothFive));
    return this;
  }
}

export class CarAnalyzeWhereBuilder {
  private where: any[] = [];
  constructor(private params: CarAnalzeWhereBuilderParams) {}

  searchId() {
    const { oddId, acbId } = this.params;
    if (oddId !== undefined) {
      this.where.push(eq(tnAnalysisCarBasic.oddId, oddId));
    }
    if (acbId !== undefined) {
      this.where.push(eq(tnAnalysisCarDetail.acbId, acbId));
    }

    return this;
  }

  searchDate() {
    const { oddDate: date, acbTreatStartDate } = this.params;
    if (acbTreatStartDate !== undefined) {
      this.where.push(
        gte(tnAnalysisCarBasic.acbTreatStartDate, acbTreatStartDate),
      );
    }

    return this;
  }

  build() {
    return and(...this.where);
  }
}

//------------------------- B2C Analyze Where Builder -------------------------//
interface B2cAnalyzeWhereBuilderParams {
  bodId?: number;
  basbId?: number;
  dto?: GetAnalysisDetailListDto;
}

export class B2cAnalyzeWhereBuilder {
  private where: any[] = [];
  constructor(private params: B2cAnalyzeWhereBuilderParams) {}

  searchId() {
    const { bodId, basbId } = this.params;
    if (bodId !== undefined) {
      this.where.push(eq(tnB2cAnalysisBasic.bodId, bodId));
    }
    if (basbId !== undefined) {
      this.where.push(eq(tnB2cAnalysisDetail.basbId, basbId));
    }
    return this;
  }

  ansType(listType: 'basic' | 'detail') {
    if (listType === 'detail') {
      this.detailAnsType();
    }
    return this;
  }

  build() {
    return and(...this.where);
  }

  //------------------------- private -------------------------//
  private detailAnsType() {
    const { dto } = this.params;
    if (!dto) {
      throw new Error('필수 파라미터 누락 (dto)');
    }
    const detailDto = dto as GetAnalysisDetailListDto;
    const { ansType } = detailDto;

    switch (ansType) {
      case 'ANS001': {
        // 수술, 검사, 투약만
        const inNotNullColumn = or(
          isNotNull(tnB2cAnalysisDetail.basdOperation),
          isNotNull(tnB2cAnalysisDetail.basdExamination),
        );
        this.where.push(inNotNullColumn);
        break;
      }
      case 'ANS002': {
        // 1년이내 검사
        this.ans002();
        break;
      }
      case 'ANS004': {
        // 5년이내 입원/수술
        const inNotNullColumn = isNotNull(tnB2cAnalysisDetail.basdOperation);
        this.where.push(inNotNullColumn);
        break;
      }
      case 'ANS007': {
        // 숨은보험(치아)
        this.ans007();
        break;
      }
      case 'ANS008': {
        // 숨은보험(수술)
        this.ans008();
        break;
      }
      case 'ANS009': {
        // 숨은보험(기타)
        this.ans009();
        break;
      }
      case 'ANS010': {
        // 치아고지(틀니)
        this.ans010();
        break;
      }
      case 'ANS011': {
        // 치아고지(1개)
        this.ans011();
        break;
      }
      case 'ANS012': {
        // 치아고지(5개)
        this.ans012();
        break;
      }
      default:
        break;
    }
    return this;
  }

  private ans002() {
    const inNotNullColumn = isNotNull(tnB2cAnalysisDetail.basdExamination);
    this.where.push(inNotNullColumn);
  }

  private ans007() {
    const inNotNullColumn = isNotNull(tnB2cAnalysisDetail.basdInTooth);
    this.where.push(inNotNullColumn);
  }

  private ans008() {
    const inNotNullColumn = isNotNull(tnB2cAnalysisDetail.basdInOperation);
    this.where.push(inNotNullColumn);
  }

  private ans009() {
    const inNotNullColumn = isNotNull(tnB2cAnalysisDetail.basdInEtc);
    this.where.push(inNotNullColumn);
  }

  private ans010() {
    const inNotNullColumn = isNotNull(tnB2cAnalysisDetail.basdDentures);
    this.where.push(inNotNullColumn);
  }

  private ans011() {
    const inNotNullColumn = isNotNull(tnB2cAnalysisDetail.basdToothOne);
    this.where.push(inNotNullColumn);
  }

  private ans012() {
    const inNotNullColumn = isNotNull(tnB2cAnalysisDetail.basdToothFive);
    this.where.push(inNotNullColumn);
  }
}
