import { AdminJwtAuthGuard } from '@api/modules/auth/admin/jwt.guard';
import {
  Body,
  Controller,
  ForbiddenException,
  Get,
  Header,
  Param,
  Post,
  Query,
  Req,
  Res,
  StreamableFile,
  UseGuards,
} from '@nestjs/common';
import { ApiOkResponse, ApiOperation, ApiTags } from '@nestjs/swagger';
import { plainToInstance } from 'class-transformer';
import { Response } from 'express';
import {
  AgencySettlementBatchResultDto,
  AgencySettlementPaidCompleteDto,
  AgencySettlementPaymentDto,
  AgencySettlementTaxInvoiceDto,
  AgencySettleIdsDto,
  GetAgencySettlementDetailDto,
  GetAgencySettlementListDto,
  GetAgencySettlementListResponseDto,
  GetAgencySettlementOrderListDto,
  GetAgencySettlementOrderListResponseDto,
  SendAgencySettlementMailDto,
  UpdateAgencySettlementOrderActionDto,
  UpdateAgencySettlementOrderPriceDto,
} from './settlement-admin.dto';
import { AdminAgencySettlementService } from './settlement-admin.service';
import { AgencySettleStatus } from './settlement.entity';
import { RolesGuard } from '@api/modules/auth/admin/roles.guard';
import { Roles } from '@api/modules/auth/admin/roles.decorator';
import { CorpReadonlyGuard } from '@api/modules/auth/admin/corp-readonly.guard';

@ApiTags('[관리자] 기업 정산')
@UseGuards(AdminJwtAuthGuard, RolesGuard, CorpReadonlyGuard)
@Roles('admin')
@Controller('admin/agency-settlements')
export class AgencySettlementAdminController {
  constructor(
    private adminAgencySettlementService: AdminAgencySettlementService,
  ) {}

  //* 기업관리자(corp) 정산 조회 가드 — 정산은 agcId 집계라 org 분해 불가.
  //*   조직관리자(ORG)는 조회 불가(403), 최고관리자(SUPER)만 자기 기업 정산 조회.
  //*   corp 가 아니면 null(=admin 통과).
  private assertCorpSuper(req: Request): { agcId: number } | null {
    const admin = req['admin'];
    if (admin?.role !== 'corp') return null;
    if (admin.corpScope === 'ORG') {
      throw new ForbiddenException('조직관리자는 정산을 조회할 수 없습니다');
    }
    return { agcId: admin.agcId };
  }

  @ApiOperation({
    summary: '기업 정산 목록/검색',
  })
  @Get()
  @Roles('admin', 'corp')
  @ApiOkResponse({ type: GetAgencySettlementListResponseDto })
  async getAgencySettlementList(
    @Query() query: GetAgencySettlementListDto,
    @Req() req: Request,
  ) {
    //* corp(SUPER)는 자기 기업 정산만 + 정산확정 항목만 노출(2026-07 기획, company-mockup)
    const corp = this.assertCorpSuper(req);
    if (corp) {
      query.agcId = corp.agcId;
      query.settleStatus = AgencySettleStatus.CONFIRMED;
    }

    const data =
      await this.adminAgencySettlementService.getAgencySettlementList(query);
    return plainToInstance(GetAgencySettlementListResponseDto, data, {
      excludeExtraneousValues: true,
    });
  }

  @Get(':astId')
  @Roles('admin', 'corp')
  @ApiOperation({
    summary: '기업 정산 상세조회',
  })
  @ApiOkResponse({ type: GetAgencySettlementDetailDto })
  async getAgencySettlement(
    @Param('astId') astId: number,
    @Req() req: Request,
  ) {
    const corp = this.assertCorpSuper(req);
    if (corp) {
      await this.adminAgencySettlementService.assertCorpOwner(
        astId,
        corp.agcId,
      );
    }
    const data = await this.adminAgencySettlementService.getDetail(astId);
    return plainToInstance(GetAgencySettlementDetailDto, data, {
      excludeExtraneousValues: true,
    });
  }

  @Get(':astId/orders')
  @Roles('admin', 'corp')
  @ApiOperation({
    summary: `기업 정산 주문 목록 ('/admin/order' 과 동일한 응답값)`,
  })
  @ApiOkResponse({ type: GetAgencySettlementOrderListResponseDto })
  async getAgencySettlementOrderList(
    @Param('astId') astId: number,
    @Query() query: GetAgencySettlementOrderListDto,
    @Req() req: Request,
  ) {
    const corp = this.assertCorpSuper(req);
    if (corp) {
      await this.adminAgencySettlementService.assertCorpOwner(
        astId,
        corp.agcId,
      );
    }
    const data =
      await this.adminAgencySettlementService.getAgencySettlementOrderList(
        astId,
        query,
      );
    return plainToInstance(GetAgencySettlementOrderListResponseDto, data, {
      excludeExtraneousValues: true,
    });
  }

  @Get(':astId/orders/excel')
  @Roles('admin', 'corp')
  @ApiOperation({
    summary: '기업 정산 구독내역 엑셀 다운로드',
  })
  @Header(
    'Content-Type',
    'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
  )
  @Header(
    'Content-Disposition',
    `attachment;filename*=utf-8''${encodeURIComponent('구독내역')}.xlsx`,
  )
  async downloadAgencySettlementOrderExcel(
    @Param('astId') astId: number,
    @Res({ passthrough: true }) res: Response,
    @Req() req: Request,
  ) {
    const corp = this.assertCorpSuper(req);
    if (corp) {
      await this.adminAgencySettlementService.assertCorpOwner(
        astId,
        corp.agcId,
      );
    }
    await this.adminAgencySettlementService.downloadOrderExcel(
      astId,
      res,
      !!corp,
    );
    return;
  }

  @Post('paid/complete')
  @ApiOperation({
    summary:
      '기업 정산 입금완료처리 (입금일 = paidAt ?? astPaidAt ?? 오늘, 이미 완료 건 skip)',
  })
  @ApiOkResponse({ type: AgencySettlementBatchResultDto })
  async completeAgencySettlementPayment(
    @Body() body: AgencySettlementPaidCompleteDto,
  ) {
    return await this.adminAgencySettlementService.completeAgencySettlementPayment(
      body,
    );
  }

  @Post('paid/cancel')
  @ApiOperation({
    summary: '기업 정산 입금철회처리 (미완료 건 skip)',
  })
  @ApiOkResponse({ type: AgencySettlementBatchResultDto })
  async cancelAgencySettlementPayment(
    @Body() body: AgencySettlementPaymentDto,
  ) {
    return await this.adminAgencySettlementService.cancelAgencySettlementPayment(
      body,
    );
  }

  @Post('settle/confirm')
  @ApiOperation({
    summary:
      '기업 정산확정 (일괄/단건 공용) — PENDING 건만 확정, 이미 확정 건 skip',
  })
  @ApiOkResponse({ type: AgencySettlementBatchResultDto })
  async confirmAgencySettlement(
    @Body() body: AgencySettleIdsDto,
    @Req() req: Request,
  ) {
    const admin = req['admin'];
    return await this.adminAgencySettlementService.confirmSettlement(
      body,
      admin.admId,
    );
  }

  @Post('settle/cancel')
  @ApiOperation({
    summary: '기업 정산확정 해제 — CONFIRMED 건만 해제, 미정산 건 skip',
  })
  @ApiOkResponse({ type: AgencySettlementBatchResultDto })
  async cancelAgencySettlementConfirm(@Body() body: AgencySettleIdsDto) {
    return await this.adminAgencySettlementService.cancelSettlementConfirm(
      body,
    );
  }

  @Post('tax-invoice')
  @ApiOperation({
    summary:
      '기업 정산 세금계산서 발행여부 수기 기록 (실제 발행 연동 범위 외) — 미존재 건 skip',
  })
  @ApiOkResponse({ type: AgencySettlementBatchResultDto })
  async updateAgencySettlementTaxInvoice(
    @Body() body: AgencySettlementTaxInvoiceDto,
  ) {
    return await this.adminAgencySettlementService.updateTaxInvoice(body);
  }

  @Post(':astId/orders/settle-action')
  @ApiOperation({
    summary:
      '기업 정산 주문 일괄 반영변경 (INCLUDE/EXCLUDE/CARRYOVER) — PENDING 정산만, 주문취소 건 skip',
  })
  async updateAgencySettlementOrderAction(
    @Param('astId') astId: number,
    @Body() body: UpdateAgencySettlementOrderActionDto,
  ) {
    return await this.adminAgencySettlementService.updateSettleAction(
      astId,
      body,
    );
  }

  @Post(':astId/orders/settlement-price')
  @ApiOperation({
    summary:
      '기업 정산 주문 정산단가 일괄수정 — PENDING 정산만, 주문취소 건 skip. 전월 구독수 기준 단가 변동 대응(결제금액 불변)',
  })
  async updateAgencySettlementOrderPrice(
    @Param('astId') astId: number,
    @Body() body: UpdateAgencySettlementOrderPriceDto,
  ) {
    return await this.adminAgencySettlementService.updateOrderSettlementPrice(
      astId,
      body,
    );
  }

  @Post(':astId/mail')
  @ApiOperation({
    summary:
      '기업 정산 안내 메일 발송 — CONFIRMED 정산만, 구매내역서 첨부 옵션, 발송 이력 기록',
  })
  async sendAgencySettlementMail(
    @Param('astId') astId: number,
    @Body() body: SendAgencySettlementMailDto,
    @Req() req: Request,
  ) {
    const admin = req['admin'];
    return await this.adminAgencySettlementService.sendSettlementMail(
      astId,
      body,
      admin.admId,
    );
  }

  @Get(':astId/statement')
  @Roles('admin', 'corp')
  @ApiOperation({
    summary:
      '기업 정산 구매내역서 PDF 다운로드 (A4 1장) — CONFIRMED 정산만, 확정금액(반영분) 기준',
  })
  async downloadAgencySettlementStatement(
    @Param('astId') astId: number,
    @Res({ passthrough: true }) res: Response,
    @Req() req: Request,
  ) {
    const corp = this.assertCorpSuper(req);
    if (corp) {
      await this.adminAgencySettlementService.assertCorpOwner(
        astId,
        corp.agcId,
      );
    }
    const { buffer, filename } =
      await this.adminAgencySettlementService.buildStatement(astId);

    res.setHeader('Content-Type', 'application/pdf');
    res.setHeader(
      'Content-Disposition',
      `attachment;filename*=utf-8''${encodeURIComponent(filename)}`,
    );
    return new StreamableFile(buffer);
  }
}
