import {
  BadRequestException,
  Body,
  Controller,
  Get,
  Param,
  Post,
  Query,
  Req,
  Res,
  UploadedFile,
  UseInterceptors,
} from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express';
import type { Request, Response } from 'express';
import { RequirePermission } from '../auth/decorators/permission.decorator';
import type { AuthenticatedUser } from '../authorization';
import { extractRequestInfo } from '../common/request-utils';
import { OperationLogsService } from '../operation-logs/operation-logs.service';
import {
  IMPORT_STEP_KEYS,
  type ImportRowDecision,
  type ImportStageRequest,
  type ImportStepKey,
} from './imports.types';
import { ImportsService } from './imports.service';

interface AuthenticatedRequest extends Request {
  user: AuthenticatedUser;
}

const IMPORT_GATE_PERMISSIONS = [
  'student:import',
  'room:create',
  'room:edit',
  'occupancy:checkin',
  'occupancy:transfer',
] as const;

@Controller('imports')
@RequirePermission('ai:chat:use', ...IMPORT_GATE_PERMISSIONS)
export class ImportsController {
  constructor(
    private readonly importsService: ImportsService,
    private readonly opLog: OperationLogsService,
  ) {}

  @Post('runs')
  @UseInterceptors(FileInterceptor('file', { limits: { fileSize: 10 * 1024 * 1024 } }))
  async create(
    @Req() req: AuthenticatedRequest,
    @UploadedFile() file: Express.Multer.File | undefined,
    @Body() body: Record<string, unknown>,
  ) {
    if (!file) throw new BadRequestException('缺少上传文件');
    let stages: ImportStageRequest[] | undefined;
    if (typeof body.stages === 'string' && body.stages.trim()) {
      try {
        const parsed = JSON.parse(body.stages) as unknown;
        if (!Array.isArray(parsed)) throw new Error('not array');
        stages = parsed as ImportStageRequest[];
      } catch {
        throw new BadRequestException('stages 参数格式错误');
      }
    }
    let mapping: Partial<Record<ImportStepKey, Record<string, string>>> | undefined;
    if (typeof body.mapping === 'string' && body.mapping.trim()) {
      try {
        const parsed = JSON.parse(body.mapping) as unknown;
        if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
          mapping = parsed;
        }
      } catch {
        throw new BadRequestException('mapping 参数格式错误');
      }
    }
    let settings: Record<string, unknown> | undefined;
    if (typeof body.settings === 'string' && body.settings.trim()) {
      try {
        const parsed = JSON.parse(body.settings) as unknown;
        if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
          settings = parsed as Record<string, unknown>;
        }
      } catch {
        throw new BadRequestException('settings 参数格式错误');
      }
    }
    const conversationId =
      body.conversationId !== undefined ? Number(body.conversationId) : undefined;
    const source = body.source === 'ai' ? 'ai' : 'manual';
    const data = await this.importsService.createRun(
      this.principal(req.user),
      source,
      {
        originalName: file.originalname,
        mimeType: file.mimetype,
        size: file.size,
        buffer: file.buffer,
      },
      Number.isFinite(conversationId) ? conversationId : undefined,
      stages,
      mapping,
      settings,
    );
    return { success: true, data };
  }

  @Get('runs/:id')
  async get(@Req() req: AuthenticatedRequest, @Param('id') id: string) {
    return { success: true, data: await this.importsService.getRun(req.user.id, id) };
  }

  @Post('runs/:id/steps/:stepKey/preview')
  async preview(
    @Req() req: AuthenticatedRequest,
    @Param('id') id: string,
    @Param('stepKey') stepKey: string,
    @Body() body: { sheets?: string[]; mapping?: Record<string, string> },
  ) {
    const data = await this.importsService.previewStep(
      this.principal(req.user),
      id,
      this.parseStepKey(stepKey),
      body ?? {},
    );
    return { success: true, data };
  }

  @Post('runs/:id/steps/:stepKey/commit')
  async commit(
    @Req() req: AuthenticatedRequest,
    @Param('id') id: string,
    @Param('stepKey') stepKey: string,
    @Body() body: { decisions?: ImportRowDecision[] },
  ) {
    const data = await this.importsService.commitStep(
      this.principal(req.user),
      id,
      this.parseStepKey(stepKey),
      Array.isArray(body?.decisions) ? body.decisions : [],
    );
    if (data.status === 'committed') {
      const { ipAddress, userAgent } = extractRequestInfo(req);
      await this.opLog.log({
        userId: req.user.id,
        username: req.user.username,
        module: '批量导入',
        action: '提交导入阶段',
        detail: data.message,
        targetType: 'import_run',
        ipAddress,
        userAgent,
      });
    }
    return { success: true, data };
  }

  @Get('runs/:id/report')
  async report(
    @Req() req: AuthenticatedRequest,
    @Res() res: Response,
    @Param('id') id: string,
    @Query('stepKey') stepKey?: string,
  ) {
    const { filename, buffer } = await this.importsService.errorReport(
      req.user.id,
      id,
      stepKey ? this.parseStepKey(stepKey) : undefined,
    );
    res.setHeader('Content-Type', 'text/csv; charset=utf-8');
    res.setHeader(
      'Content-Disposition',
      `attachment; filename*=UTF-8''${encodeURIComponent(filename || 'import-errors.csv')}`,
    );
    res.setHeader('Content-Length', String(buffer.length));
    res.send(buffer);
  }

  private parseStepKey(value: string): ImportStepKey {
    if ((IMPORT_STEP_KEYS as readonly string[]).includes(value)) {
      return value as ImportStepKey;
    }
    throw new BadRequestException(`未知导入阶段：${value}`);
  }

  private principal(user: AuthenticatedUser): {
    id: number;
    permissions: string[];
    isSuperAdmin: boolean;
  } {
    return {
      id: user.id,
      permissions: user.permissions,
      isSuperAdmin: user.isSuperAdmin,
    };
  }
}
