import {
  BadRequestException,
  Body,
  Controller,
  ForbiddenException,
  Get,
  NotFoundException,
  Param,
  ParseIntPipe,
  Post,
  Query,
  Req,
  Res,
  UseGuards,
} from '@nestjs/common';
import { ApiOkResponse, ApiOperation, ApiTags } from '@nestjs/swagger';
import { plainToInstance } from 'class-transformer';
import { Request, Response } from 'express';
import { JwtAuthGuard } from '../auth/user/jwt.guard';
import { OwnAnalysisGuard } from '../auth/user/own-analysis.guard';
import { CarViewAdminGuard } from '../auth/admin/car-view-admin.guard';
import {
  GetAnalysisAggregateListDto,
  GetAnalysisAggregateListResponseDto,
  GetAnalysisBasicListDto,
  GetAnalysisBasicListResponseDto,
  GetAnalysisCarBasicListDto,
  GetAnalysisCarBasicListResponseDto,
  GetAnalysisCarDetailListDto,
  GetAnalysisCarDetailListResponseDto,
  GetAnalysisDetailListDto,
  GetAnalysisDetailListResponseDto,
  GetAnalysisHiddenInsuranceListResponseDto,
  GetAnalysisPrescibeListResponseDto,
  GetAnalysisPrescribeListDto,
  GetDiseasePredictionPdfResponseDto,
  VerifyB2cBirthDto,
  VerifyB2cBirthResponseDto,
} from './analyze.dto';
import { AnalyzeRepository } from './analyze.repository';
import { AnalyzeService } from './analyze.service';

@ApiTags('분석상세')
//* oddId만 바꿔 남의 분석(고객 개인정보·진료이력)을 조회할 수 있어 소유자 검증을 추가한다.
@UseGuards(JwtAuthGuard, OwnAnalysisGuard)
@Controller('analyze')
export class AnalyzeController {
  constructor(
    private analyzeService: AnalyzeService,
    private analyzeRepository: AnalyzeRepository,
  ) {}

  @ApiOperation({
    summary: '분석데이터(기본)',
    description: `분석기본데이터를 요청합니다.`,
  })
  @Get(':oddId/basic')
  @ApiOkResponse({ type: GetAnalysisBasicListResponseDto })
  async getBasicList(
    @Param('oddId', ParseIntPipe) oddId: number,
    @Query() dto: GetAnalysisBasicListDto,
  ) {
    const data = await this.analyzeService.getBasicList(oddId, dto);
    return plainToInstance(GetAnalysisBasicListResponseDto, data, {
      excludeExtraneousValues: true,
    });
  }

  @ApiOperation({
    summary: '분석데이터(상세)',
    description: `분석상세데이터를 요청합니다.`,
  })
  @Get(':asbId/detail')
  @ApiOkResponse({ type: GetAnalysisDetailListResponseDto })
  async getDetailList(
    @Param('asbId', ParseIntPipe) asbId: number,
    @Query() dto: GetAnalysisDetailListDto,
  ) {
    const data = await this.analyzeService.getDetailList(asbId, dto);
    return plainToInstance(GetAnalysisDetailListResponseDto, data, {
      excludeExtraneousValues: true,
    });
  }

  @ApiOperation({
    summary: `분석데이터(5년치계산분)`,
    description: `분석기본데이터 중 계산이 필요한 데이터를 요청합니다.
                  - 5년내 7일이상 진료/30일이상 투약의 기본
                  - 5년내 11(6대)질병진단/수술의 기본
                  - 숨은보험진단의 기본
                  - 주의사항의 기본`,
  })
  @Get(':oddId/aggregate')
  @ApiOkResponse({ type: GetAnalysisAggregateListResponseDto })
  async getAggregateList(
    @Param('oddId', ParseIntPipe) oddId: number,
    @Query() dto: GetAnalysisAggregateListDto,
  ) {
    const data = await this.analyzeService.getAggregateList(oddId, dto);
    return plainToInstance(GetAnalysisAggregateListResponseDto, data, {
      excludeExtraneousValues: true,
    });
  }

  @ApiOperation({
    summary: `처방조제정보`,
    description: `중복투약일 경우 해당일의 처방조제정보를 요청합니다.`,
  })
  @Get(':oddId/prescribe')
  @ApiOkResponse({ type: GetAnalysisPrescibeListResponseDto })
  async getPrescribeList(
    @Param('oddId', ParseIntPipe) oddId: number,
    @Query() dto: GetAnalysisPrescribeListDto,
  ) {
    const data = await this.analyzeService.getPrescribeList(oddId, dto);
    return plainToInstance(GetAnalysisPrescibeListResponseDto, data, {
      excludeExtraneousValues: true,
    });
  }

  @ApiOperation({
    summary: `분석데이터(자동차보험) 목록`,
    description: `자동차보험 분석데이터를 요청합니다.`,
  })
  @Get(':oddId/car-basic')
  @ApiOkResponse({ type: GetAnalysisCarBasicListResponseDto })
  async getCarBasicList(
    @Param('oddId', ParseIntPipe) oddId: number,
    @Query() dto: GetAnalysisCarBasicListDto,
    @Req() req: Request,
  ) {
    const data = await this.analyzeService.getCarBasicList(
      oddId,
      dto,
      Number(req['user']?.memId ?? -1),
    );
    return plainToInstance(GetAnalysisCarBasicListResponseDto, data, {
      excludeExtraneousValues: true,
    });
  }

  @ApiOperation({
    summary: '분석데이터(자동차보험) 상세',
    description: `자동차보험 분석상세데이터를 요청합니다.`,
  })
  @Get(':acbId/car-detail')
  @ApiOkResponse({ type: GetAnalysisCarDetailListResponseDto })
  async getCarDetailList(
    @Param('acbId', ParseIntPipe) acbId: number,
    @Query() dto: GetAnalysisCarDetailListDto,
    @Req() req: Request,
  ) {
    const data = await this.analyzeService.getCarDetailList(
      acbId,
      dto,
      Number(req['user']?.memId ?? -1),
    );
    return plainToInstance(GetAnalysisCarDetailListResponseDto, data, {
      excludeExtraneousValues: true,
    });
  }

  @ApiOperation({
    summary: '분석데이터(숨은보험금) 전체 기본 목록',
    description: '분석데이터(숨은보험금) 전체 기본 목록을 요청합니다.',
  })
  @Get(':oddId/hidden-insurance')
  @ApiOkResponse({ type: GetAnalysisHiddenInsuranceListResponseDto })
  async getHiddenInsuranceList(@Param('oddId', ParseIntPipe) oddId: number) {
    const data = await this.analyzeService.getHiddenInsuranceList(oddId);
    return plainToInstance(GetAnalysisHiddenInsuranceListResponseDto, data, {
      excludeExtraneousValues: true,
    });
  }

  @ApiOperation({
    summary: '병명예측 PDF 데이터',
    description: '병명예측 탭의 전체 데이터를 PDF 생성용으로 제공합니다.',
  })
  @Get(':oddId/disease-prediction-pdf')
  @ApiOkResponse({ type: GetDiseasePredictionPdfResponseDto })
  async getDiseasePredictionPdf(@Param('oddId', ParseIntPipe) oddId: number) {
    const data = await this.analyzeService.getDiseasePredictionPdf(oddId);
    return plainToInstance(GetDiseasePredictionPdfResponseDto, data, {
      excludeExtraneousValues: true,
    });
  }

  @ApiOperation({
    summary: '주의사항 PDF 데이터',
    description: '주의사항 탭의 전체 데이터를 PDF 생성용으로 제공합니다.',
  })
  @Get(':oddId/caution-pdf')
  async getCautionPdfList(@Param('oddId', ParseIntPipe) oddId: number) {
    return await this.analyzeService.getCautionPdfList(oddId);
  }

  @ApiOperation({
    summary: '건강체 PDF 데이터',
    description: '건강체 탭의 전체 데이터를 PDF 생성용으로 제공합니다.',
  })
  @Get(':oddId/healthy-pdf')
  async getHealthyPdfList(@Param('oddId', ParseIntPipe) oddId: number) {
    return await this.analyzeService.getHealthyPdfList(oddId);
  }

  @ApiOperation({
    summary: '유병자 PDF 데이터',
    description: '유병자 탭의 전체 데이터를 PDF 생성용으로 제공합니다.',
  })
  @Get(':oddId/sick-pdf')
  async getSickPdfList(@Param('oddId', ParseIntPipe) oddId: number) {
    return await this.analyzeService.getSickPdfList(oddId);
  }

  @ApiOperation({
    summary: '5년 내 7일이상 진료 중분류 그룹 조회',
    description: '진료 기록을 중분류별로 그루핑하여 내원일수 합계 7일 이상인 그룹만 반환합니다.',
  })
  @Get(':oddId/treatment-7day-groups')
  async getTreatment7DayGroups(@Param('oddId', ParseIntPipe) oddId: number) {
    return await this.analyzeService.getTreatment7DayGroups(oddId);
  }

  @ApiOperation({
    summary: '5년 내 30일이상 투약 카테고리 그룹 조회',
    description: '처방 데이터를 약효 카테고리별로 그루핑하여 투약일수 합계 30일 이상인 카테고리만 반환합니다.',
  })
  @Get(':oddId/dosing-30day-groups')
  async getDosing30DayGroups(@Param('oddId', ParseIntPipe) oddId: number) {
    return await this.analyzeService.getDosing30DayGroups(oddId);
  }

  @ApiOperation({
    summary: '동일 진단코드 내원 그룹 조회',
    description: '5년 이내 진료 기록을 주상병코드별로 그루핑하여 2건 이상인 그룹만 반환합니다 (약국 포함).',
  })
  @Get(':oddId/disease-code-visit-groups')
  async getDiseaseCodeVisitGroups(@Param('oddId', ParseIntPipe) oddId: number) {
    return await this.analyzeService.getDiseaseCodeVisitGroups(oddId);
  }

  @ApiOperation({
    summary: '기타 숨은보험금 그룹 조회 (ANS009)',
    description: '기타 숨은보험금(asdInEtc)별로 그루핑하여 인라인 렌더링용 데이터를 반환합니다.',
  })
  @Get(':oddId/etc-insurance-groups')
  async getEtcInsuranceGroups(@Param('oddId', ParseIntPipe) oddId: number) {
    return await this.analyzeService.getEtcInsuranceGroups(oddId);
  }

  @ApiOperation({
    summary: '치아 숨은보험금 그룹 조회 (ANS007)',
    description: '치아 숨은보험금(asdInTooth)별로 그루핑하여 인라인 렌더링용 데이터를 반환합니다.',
  })
  @Get(':oddId/tooth-insurance-groups')
  async getToothInsuranceGroups(@Param('oddId', ParseIntPipe) oddId: number) {
    return await this.analyzeService.getToothInsuranceGroups(oddId);
  }
}

// B2C 전용 컨트롤러 (인증 없음)
@ApiTags('B2C 분석')
@Controller('analyze/b2c')
export class B2cAnalyzeController {
  constructor(
    private analyzeService: AnalyzeService,
    private analyzeRepository: AnalyzeRepository,
  ) {}

  private assertB2cAuth(req: Request, bodId: number) {
    if (req.cookies?.[`b2c_auth_${bodId}`] !== 'verified') {
      throw new ForbiddenException('B2C 인증이 필요합니다.');
    }
  }

  @ApiOperation({
    summary: 'B2C 사용자 정보 및 주문 정보',
    description: 'bcuId로 B2C 사용자 정보와 bodId를 조회합니다.',
  })
  @Get('user/:bcuId')
  async getB2cUser(@Param('bcuId', ParseIntPipe) bcuId: number) {
    void bcuId;
    throw new NotFoundException('B2C 사용자를 찾을 수 없습니다.');
  }

  @ApiOperation({
    summary: 'B2C 분석데이터(기본)',
    description: 'B2C 사용자의 분석기본데이터를 요청합니다. bodId를 사용합니다.',
  })
  @Get(':bodId/basic')
  @ApiOkResponse({ type: GetAnalysisBasicListResponseDto })
  async getB2cBasicList(
    @Param('bodId', ParseIntPipe) bodId: number,
    @Query() dto: GetAnalysisBasicListDto,
    @Req() req: Request,
  ) {
    this.assertB2cAuth(req, bodId);
    const data = await this.analyzeService.getBasicList(bodId, dto, true); // isB2c = true
    return plainToInstance(GetAnalysisBasicListResponseDto, data, {
      excludeExtraneousValues: true,
    });
  }

  @ApiOperation({
    summary: 'B2C 분석데이터(집계)',
    description: 'B2C 사용자의 분석집계데이터를 요청합니다. ANS003, ANS005, ANS006, ANS013 지원',
  })
  @Get(':bodId/aggregate')
  @ApiOkResponse({ type: GetAnalysisAggregateListResponseDto })
  async getB2cAggregateList(
    @Param('bodId', ParseIntPipe) bodId: number,
    @Query() dto: GetAnalysisAggregateListDto,
    @Req() req: Request,
  ) {
    this.assertB2cAuth(req, bodId);
    const data = await this.analyzeService.getAggregateList(bodId, dto, true); // isB2c = true
    return plainToInstance(GetAnalysisAggregateListResponseDto, data, {
      excludeExtraneousValues: true,
    });
  }

  @ApiOperation({
    summary: 'B2C 처방조제정보',
    description: 'B2C 사용자의 중복투약일 경우 해당일의 처방조제정보를 요청합니다.',
  })
  @Get(':bodId/prescribe')
  @ApiOkResponse({ type: GetAnalysisPrescibeListResponseDto })
  async getB2cPrescribeList(
    @Param('bodId', ParseIntPipe) bodId: number,
    @Query() dto: GetAnalysisPrescribeListDto,
    @Req() req: Request,
  ) {
    this.assertB2cAuth(req, bodId);
    const data = await this.analyzeService.getPrescribeList(bodId, dto, true); // isB2c = true
    return plainToInstance(GetAnalysisPrescibeListResponseDto, data, {
      excludeExtraneousValues: true,
    });
  }

  @ApiOperation({
    summary: 'B2C 숨은보험금 전체 데이터',
    description: 'B2C 사용자의 숨은보험금(진단/수술/치아/기타) 전체 데이터를 요청합니다.',
  })
  @Get(':bodId/hidden-insurance')
  @ApiOkResponse({ type: GetAnalysisHiddenInsuranceListResponseDto })
  async getB2cHiddenInsuranceList(@Param('bodId', ParseIntPipe) bodId: number, @Req() req: Request) {
    this.assertB2cAuth(req, bodId);
    const data = await this.analyzeService.getHiddenInsuranceList(bodId, true); // isB2c = true
    // B2C는 bab*, bad*, bcb* prefix를 사용하므로 DTO 변환 없이 그대로 반환
    return data;
  }

  @ApiOperation({
    summary: 'B2C 주의사항 PDF 데이터',
    description: 'B2C 사용자의 주의사항 전체 데이터를 PDF 생성용으로 제공합니다.',
  })
  @Get(':bodId/caution-pdf')
  async getB2cCautionPdfList(@Param('bodId', ParseIntPipe) bodId: number, @Req() req: Request) {
    this.assertB2cAuth(req, bodId);
    return await this.analyzeService.getCautionPdfList(bodId, true);
  }

  @ApiOperation({
    summary: 'B2C 건강체 PDF 데이터',
    description: 'B2C 사용자의 건강체 전체 데이터를 PDF 생성용으로 제공합니다.',
  })
  @Get(':bodId/healthy-pdf')
  async getB2cHealthyPdfList(@Param('bodId', ParseIntPipe) bodId: number, @Req() req: Request) {
    this.assertB2cAuth(req, bodId);
    return await this.analyzeService.getHealthyPdfList(bodId, true);
  }

  @ApiOperation({
    summary: 'B2C 유병자 PDF 데이터',
    description: 'B2C 사용자의 유병자 전체 데이터를 PDF 생성용으로 제공합니다.',
  })
  @Get(':bodId/sick-pdf')
  async getB2cSickPdfList(@Param('bodId', ParseIntPipe) bodId: number, @Req() req: Request) {
    this.assertB2cAuth(req, bodId);
    return await this.analyzeService.getSickPdfList(bodId, true);
  }

  @ApiOperation({
    summary: 'B2C 5년 내 7일이상 진료 중분류 그룹 조회',
    description: 'B2C 진료 기록을 중분류별로 그루핑하여 내원일수 합계 7일 이상인 그룹만 반환합니다.',
  })
  @Get(':bodId/treatment-7day-groups')
  async getB2cTreatment7DayGroups(@Param('bodId', ParseIntPipe) bodId: number, @Req() req: Request) {
    this.assertB2cAuth(req, bodId);
    return await this.analyzeService.getTreatment7DayGroups(bodId, true);
  }

  @ApiOperation({
    summary: 'B2C 5년 내 30일이상 투약 카테고리 그룹 조회',
    description: 'B2C 처방 데이터를 약효 카테고리별로 그루핑하여 투약일수 합계 30일 이상인 카테고리만 반환합니다.',
  })
  @Get(':bodId/dosing-30day-groups')
  async getB2cDosing30DayGroups(@Param('bodId', ParseIntPipe) bodId: number, @Req() req: Request) {
    this.assertB2cAuth(req, bodId);
    return await this.analyzeService.getDosing30DayGroups(bodId, true);
  }

  @ApiOperation({
    summary: 'B2C 동일 진단코드 내원 그룹 조회',
    description: 'B2C 5년 이내 진료 기록을 주상병코드별로 그루핑하여 2건 이상인 그룹만 반환합니다 (약국 포함).',
  })
  @Get(':bodId/disease-code-visit-groups')
  async getB2cDiseaseCodeVisitGroups(@Param('bodId', ParseIntPipe) bodId: number, @Req() req: Request) {
    this.assertB2cAuth(req, bodId);
    return await this.analyzeService.getDiseaseCodeVisitGroups(bodId, true);
  }

  @ApiOperation({
    summary: 'B2C 기타 숨은보험금 그룹 조회 (ANS009)',
    description: 'B2C 기타 숨은보험금(asdInEtc)별 그루핑 데이터를 반환합니다.',
  })
  @Get(':bodId/etc-insurance-groups')
  async getB2cEtcInsuranceGroups(@Param('bodId', ParseIntPipe) bodId: number, @Req() req: Request) {
    this.assertB2cAuth(req, bodId);
    return await this.analyzeService.getEtcInsuranceGroups(undefined, bodId);
  }

  @ApiOperation({
    summary: 'B2C 치아 숨은보험금 그룹 조회 (ANS007)',
    description: 'B2C 치아 숨은보험금(asdInTooth)별 그루핑 데이터를 반환합니다.',
  })
  @Get(':bodId/tooth-insurance-groups')
  async getB2cToothInsuranceGroups(@Param('bodId', ParseIntPipe) bodId: number, @Req() req: Request) {
    this.assertB2cAuth(req, bodId);
    return await this.analyzeService.getToothInsuranceGroups(undefined, bodId);
  }

  @ApiOperation({
    summary: 'B2C 자동차보험 데이터',
    description: 'B2C 사용자의 자동차보험 데이터를 요청합니다.',
  })
  @UseGuards(CarViewAdminGuard)
  @Get(':bodId/car-basic')
  async getB2cCarBasicList(
    @Param('bodId', ParseIntPipe) bodId: number,
    @Query('page', ParseIntPipe) page: number,
    @Req() req: Request,
  ) {
    this.assertB2cAuth(req, bodId);
    // B2C 주문 및 사용자 정보 조회
    const orderDetail = await this.analyzeRepository.checkB2cOrderDetailByBodId(bodId);
    if (!orderDetail) {
      throw new NotFoundException('B2C 주문 정보를 찾을 수 없습니다.');
    }

    const user = await this.analyzeRepository.getB2cUserById(orderDetail.bcuId!);

    // 자동차보험 데이터 조회
    const list = await this.analyzeRepository.getB2cCarBasicList(bodId, page);

    // count 계산 (전체 데이터 조회)
    const allList = await this.analyzeRepository.getB2cCarBasicList(bodId, 0);
    const count = allList.length;

    return {
      count,
      order: {
        member: {
          memLoginId: 'B2C',
        },
        user: {
          usrName: user?.bcuName || '',
          usrPhone: user?.bcuPhone || '',
          usrBirth: user?.bcuBirth || '',
        },
        orderDetail: {
          oddId: bodId,
          oddTransactionId: orderDetail.bodTransactionId,
          oddState: orderDetail.bodState,
          oddCompletedAt: orderDetail.bodCreatedAt,
        },
      },
      list,
    };
  }

  @ApiOperation({
    summary: 'B2C 자동차보험 상세 데이터',
    description: 'B2C 사용자의 자동차보험 상세 데이터를 요청합니다.',
  })
  @UseGuards(CarViewAdminGuard)
  @Get(':bcbId/car-detail')
  async getB2cCarDetailList(
    @Param('bcbId', ParseIntPipe) bcbId: number,
    @Query('page', ParseIntPipe) page: number,
    @Req() req: Request,
  ) {
    const owner = await this.analyzeRepository.getB2cCarBasicById(bcbId);
    if (!owner) {
      throw new NotFoundException('B2C 자동차보험 기본 정보를 찾을 수 없습니다.');
    }
    this.assertB2cAuth(req, owner.bodId);
    const data = await this.analyzeService.getB2cCarDetailList(bcbId, page);
    return data;
  }

  @ApiOperation({
    summary: 'B2C 진단 상세 데이터',
    description: 'B2C 사용자의 진단 상세 데이터를 요청합니다.',
  })
  @Get(':babId/detail')
  async getB2cDetailList(
    @Param('babId', ParseIntPipe) babId: number,
    @Query() dto: GetAnalysisDetailListDto,
    @Req() req: Request,
  ) {
    const owner = await this.analyzeRepository.getB2cBasicById(babId);
    if (!owner) {
      throw new NotFoundException('B2C 기본 분석 정보를 찾을 수 없습니다.');
    }
    this.assertB2cAuth(req, owner.bodId);
    const data = await this.analyzeService.getB2cDetailList(babId, dto);
    return data;
  }

  @ApiOperation({
    summary: 'B2C 생년월일 인증',
    description: 'B2C 분석 결과 조회 전 생년월일을 검증합니다. 성공 시 쿠키에 인증 상태를 저장합니다.',
  })
  @Post(':bodId/verify-birth')
  @ApiOkResponse({ type: VerifyB2cBirthResponseDto })
  async verifyB2cBirth(
    @Param('bodId', ParseIntPipe) bodId: number,
    @Body() dto: VerifyB2cBirthDto,
    @Res({ passthrough: true }) res: Response,
  ) {
    const orderDetail = await this.analyzeRepository.checkB2cOrderDetailByBodId(bodId);
    if (!orderDetail) {
      throw new NotFoundException('분석 정보를 찾을 수 없습니다.');
    }

    // 먼저 생년월일 검증
    const isValid = await this.analyzeRepository.verifyB2cBirthDate(bodId, dto.birthDate);

    if (!isValid) {
      throw new BadRequestException('생년월일이 일치하지 않습니다.');
    }

    // 생년월일이 맞으면 조회 기간 체크 (분석요청일로부터 1년)
    const createdAt = new Date(orderDetail.bodCreatedAt);
    const oneYearLater = new Date(createdAt);
    oneYearLater.setFullYear(oneYearLater.getFullYear() + 1);
    const now = new Date();

    if (now > oneYearLater) {
      return {
        success: false,
        expired: true,
        message: '조회 가능 기간이 경과하여 결과 확인이 어려워요.',
        bodId,
        agcCode: orderDetail.bodAgcCode,
      };
    }

    // 인증 성공 시 쿠키에 저장 (7일간 유효)
    const cookieName = `b2c_auth_${bodId}`;
    const cookieValue = 'verified';
    const maxAge = 7 * 24 * 60 * 60 * 1000; // 7일

    res.cookie(cookieName, cookieValue, {
      httpOnly: true,
      secure: process.env.NODE_ENV === 'production',
      maxAge,
      sameSite: 'lax',
    });

    return {
      success: true,
      message: '인증이 완료되었습니다.',
      bodId,
    };
  }

  @ApiOperation({
    summary: 'B2C 인증 상태 확인',
    description: 'B2C 사용자의 생년월일 인증 상태 및 조회 기간 만료 여부를 확인합니다.',
  })
  @Get(':bodId/check-auth')
  async checkB2cAuth(
    @Param('bodId', ParseIntPipe) bodId: number,
    @Req() req: Request,
  ) {
    const cookieName = `b2c_auth_${bodId}`;
    const isAuthenticated = req.cookies?.[cookieName] === 'verified';

    // 상담신청 상태 및 조회 기간 확인
    const orderDetail = await this.analyzeRepository.checkB2cOrderDetailByBodId(bodId);
    const consultRequested = orderDetail?.bodConsultRequested === 1;

    // 조회 기간 만료 체크 (생성일로부터 1년)
    let expired = false;
    let agcCode = '';
    if (orderDetail?.bodCreatedAt) {
      const createdAt = new Date(orderDetail.bodCreatedAt);
      const oneYearLater = new Date(createdAt);
      oneYearLater.setFullYear(oneYearLater.getFullYear() + 1);
      const now = new Date();

      if (now > oneYearLater) {
        expired = true;
        agcCode = orderDetail.bodAgcCode || '';
      }
    }

    return {
      authenticated: isAuthenticated,
      bodId,
      consultRequested,
      expired,
      agcCode,
    };
  }

  @ApiOperation({
    summary: 'B2C 사용자 생년월일 조회 (개발용)',
    description: 'bodId로 B2C 사용자의 생년월일을 조회합니다. 개발/디버깅용입니다.',
  })
  @Get(':bodId/birth-info')
  async getB2cBirthInfo(@Param('bodId', ParseIntPipe) bodId: number, @Req() req: Request) {
    this.assertB2cAuth(req, bodId);
    const orderDetail = await this.analyzeRepository.checkB2cOrderDetailByBodId(bodId);
    if (!orderDetail) {
      throw new NotFoundException('B2C 주문 정보를 찾을 수 없습니다.');
    }

    const user = await this.analyzeRepository.getB2cUserById(orderDetail.bcuId!);
    if (!user) {
      throw new NotFoundException('B2C 사용자를 찾을 수 없습니다.');
    }

    return {
      bodId,
      bcuId: user.bcuId,
      bcuName: user.bcuName,
      bcuBirth: user.bcuBirth,
      bcuPhone: user.bcuPhone,
    };
  }
}

// B2C 사용자 및 추가 API
@ApiTags('B2C 분석 추가')
@Controller('b2c/analyze')
export class B2cAnalyzeExtController {
  constructor(private analyzeService: AnalyzeService) {}

  private assertB2cAuth(req: Request, bodId: number) {
    if (req.cookies?.[`b2c_auth_${bodId}`] !== 'verified') {
      throw new ForbiddenException('B2C 인증이 필요합니다.');
    }
  }

  @ApiOperation({
    summary: 'B2C 병명예측 PDF 데이터',
    description: 'B2C 사용자의 병명예측 탭 전체 데이터를 PDF 생성용으로 제공합니다.',
  })
  @Get(':bodId/disease-prediction-pdf')
  @ApiOkResponse({ type: GetDiseasePredictionPdfResponseDto })
  async getB2cDiseasePredictionPdf(@Param('bodId', ParseIntPipe) bodId: number, @Req() req: Request) {
    this.assertB2cAuth(req, bodId);
    const data = await this.analyzeService.getDiseasePredictionPdf(bodId, true); // isB2c = true
    return plainToInstance(GetDiseasePredictionPdfResponseDto, data, {
      excludeExtraneousValues: true,
    });
  }

  @ApiOperation({
    summary: 'B2C 상담신청',
    description: 'B2C 사용자의 상담신청 상태를 업데이트합니다.',
  })
  @Post(':bodId/consult-request')
  async requestB2cConsult(@Param('bodId', ParseIntPipe) bodId: number, @Req() req: Request) {
    this.assertB2cAuth(req, bodId);
    await this.analyzeService.updateB2cConsultRequest(bodId);
    return { success: true, message: '상담 신청이 완료되었습니다.' };
  }
}

@ApiTags('B2C 사용자')
@Controller('b2c/user')
export class B2cUserController {
  @ApiOperation({
    summary: 'B2C 사용자 찾기',
    description: '전화번호, 이름, 생년월일로 B2C 사용자를 찾습니다.',
  })
  @Get('find')
  async findB2cUser(
    @Query('phone') phone: string,
    @Query('name') name: string,
    @Query('birth') birth: string,
  ) {
    void phone;
    void name;
    void birth;
    throw new NotFoundException('B2C 사용자를 찾을 수 없습니다.');
  }
}
