import { ApiResponseInterceptor } from '@api/interceptors/api-response.interceptor';
import { AdminJwtAuthGuard } from '@api/modules/auth/admin/jwt.guard';
import { Roles } from '@api/modules/auth/admin/roles.decorator';
import { RolesGuard } from '@api/modules/auth/admin/roles.guard';
import {
  Body,
  Controller,
  Delete,
  ForbiddenException,
  Get,
  Param,
  Post,
  Put,
  Query,
  Req,
  UseGuards,
  UseInterceptors,
} from '@nestjs/common';
import { Request } from 'express';
import { ApiOkResponse, ApiOperation, ApiTags } from '@nestjs/swagger';
import { plainToInstance } from 'class-transformer';
import {
  AgencyOrganizationDetailDto,
  CreateOrganizationDto,
  DeleteOrganizationResponseDto,
  GetOrganizationListDto,
  GetOrganizationListResponseDto,
  MoveOrganizationDto,
  UpdateOrganizationDto,
} from './organization.dto';
import { AgencyOrganization } from './organization.entity';
import { AgencyOrganizationService } from './organization.service';

/**
 * [관리자] 기업조직 관리 컨트롤러(task-688)
 * - 응답 포맷은 컨트롤러 단위로 ApiResponseInterceptor를 적용해
 *   { code, message, data } envelope으로 감싼다(기존 컨트롤러들과의 충돌 방지를 위해 전역 미적용).
 *   따라서 컨트롤러는 data 페이로드만 반환한다.
 */
@ApiTags('[관리자] 기업조직')
@UseInterceptors(ApiResponseInterceptor)
@UseGuards(AdminJwtAuthGuard, RolesGuard)
@Controller()
export class AdminAgencyOrganizationController {
  constructor(private organizationService: AgencyOrganizationService) {}

  //* 기업관리자(corp) 접근 검증(2026-07 기획 — 기업조직관리 화면): 최고관리자(SUPER)만,
  //*  자기 기업(agcId) 조직만 허용. 조직관리자(ORG)와 타 기업 접근은 차단.
  private assertCorpAgency(req: Request, agcId: number | undefined) {
    const admin = req['admin'];
    if (admin?.role !== 'corp') return;
    if (
      admin.corpScope === 'ORG' ||
      agcId == null ||
      Number(admin.agcId) !== Number(agcId)
    ) {
      throw new ForbiddenException('접근 권한이 없습니다');
    }
  }

  //* orgId 기반 엔드포인트용 — 대상 조직의 소속 기업을 조회해 corp 접근을 검증한다.
  private async assertCorpOrg(req: Request, orgId: string) {
    if (req['admin']?.role !== 'corp') return;
    const detail = await this.organizationService.getDetail(orgId);
    this.assertCorpAgency(req, (detail as any)?.agcId);
  }

  @ApiOperation({ summary: '기업 조직 목록 조회' })
  @Get('admin/agencies/:agcId/organizations')
  @ApiOkResponse({ type: GetOrganizationListResponseDto })
  @Roles('admin', 'sales', 'corp')
  async getList(
    @Param('agcId') agcId: number,
    @Query() query: GetOrganizationListDto,
    @Req() req: Request,
  ) {
    this.assertCorpAgency(req, +agcId);
    const data = await this.organizationService.getList(+agcId, query);
    return plainToInstance(GetOrganizationListResponseDto, data, {
      excludeExtraneousValues: true,
    });
  }

  @ApiOperation({ summary: '기업 조직 신규 등록' })
  @Post('admin/agencies/:agcId/organizations')
  @ApiOkResponse({ type: AgencyOrganization })
  @Roles('admin', 'sales', 'corp')
  async create(
    @Param('agcId') agcId: number,
    @Body() body: CreateOrganizationDto,
    @Req() req: Request,
  ) {
    this.assertCorpAgency(req, +agcId);
    const data = await this.organizationService.create(+agcId, body);
    return plainToInstance(AgencyOrganizationDetailDto, data, {
      excludeExtraneousValues: true,
    });
  }

  @ApiOperation({ summary: '기업 조직 상세 조회' })
  @Get('admin/agency-organizations/:orgId')
  @ApiOkResponse({ type: AgencyOrganizationDetailDto })
  @Roles('admin', 'sales', 'corp')
  async getDetail(@Param('orgId') orgId: string, @Req() req: Request) {
    await this.assertCorpOrg(req, orgId);
    const data = await this.organizationService.getDetail(orgId);
    return plainToInstance(AgencyOrganizationDetailDto, data, {
      excludeExtraneousValues: true,
    });
  }

  @ApiOperation({ summary: '기업 조직 수정' })
  @Put('admin/agency-organizations/:orgId')
  @ApiOkResponse({ type: AgencyOrganizationDetailDto })
  @Roles('admin', 'sales', 'corp')
  async update(
    @Param('orgId') orgId: string,
    @Body() body: UpdateOrganizationDto,
    @Req() req: Request,
  ) {
    await this.assertCorpOrg(req, orgId);
    const data = await this.organizationService.update(orgId, body);
    return plainToInstance(AgencyOrganizationDetailDto, data, {
      excludeExtraneousValues: true,
    });
  }

  @ApiOperation({
    summary: '기업 조직 이동(부분트리 재배치)',
    description:
      '자신과 모든 하위 조직의 계층(path/depth)을 재계산합니다. 자기 자신/자기 하위로의 이동은 차단됩니다. newParentId=null이면 루트로 이동.',
  })
  @Put('admin/agency-organizations/:orgId/move')
  @ApiOkResponse({ type: AgencyOrganizationDetailDto })
  @Roles('admin', 'sales', 'corp')
  async move(
    @Param('orgId') orgId: string,
    @Body() body: MoveOrganizationDto,
    @Req() req: Request,
  ) {
    await this.assertCorpOrg(req, orgId);
    const data = await this.organizationService.move(
      orgId,
      body.newParentId ?? null,
    );
    return plainToInstance(AgencyOrganizationDetailDto, data, {
      excludeExtraneousValues: true,
    });
  }

  @ApiOperation({
    summary: '기업 조직 삭제(하위 포함)',
    description:
      '대상 조직과 모든 하위 조직을 삭제합니다. 소속 회원은 삭제하지 않고 미배정(org_id=NULL)으로 전환합니다. 하위 포함 활성 계약이 있으면 차단됩니다.',
  })
  @Delete('admin/agency-organizations/:orgId')
  @ApiOkResponse({ type: DeleteOrganizationResponseDto })
  @Roles('admin', 'sales', 'corp')
  async delete(@Param('orgId') orgId: string, @Req() req: Request) {
    await this.assertCorpOrg(req, orgId);
    const data = await this.organizationService.deleteSubtree(orgId);
    return plainToInstance(DeleteOrganizationResponseDto, data, {
      excludeExtraneousValues: true,
    });
  }
}
