import {
  BadRequestException,
  Body,
  Controller,
  Get,
  HttpCode,
  HttpStatus,
  Logger,
  Post,
  Query,
  Request,
  UnauthorizedException,
} from '@nestjs/common';
import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger';
import { Auditable, Sensitive } from '@core/observability/audit/decorators/auditable.decorator';
import { PrismaService } from '@core/database/prisma/prisma.service';
import { InternalAppPlatformService } from './internal-app-platform.service';
import { InternalAppTokenService } from './services/token.service';
import { InternalAppEventsService } from './services/events.service';
import { parseIsoOrUndefined, parsePositiveInt } from '@common/utils/query-parsers';

/**
 * 内部 app 平台 HTTP API（FF AI Workspace Web 调用）
 *
 * 端点（详见 07-api.md §4）：
 * - POST /tokens         颁发 token（FF AI Workspace "我的 Apps" 页）
 * - POST /tokens/revoke  撤销当前 ACTIVE token
 * - GET  /tokens/me      查我的 token 状态
 * - GET  /me/apps        列出我的 app
 *
 * 鉴权链：
 * - 全局 JwtAuthGuard 已校验 JWT，注入 req.user.userId / id / email 等
 * - employeeSlug 通过 platformSvc.resolveEmployeeSlug() 从 binding 表反查
 *   （Entra middleware 落地后会改成直接从 req.user.employeeSlug 拿，本 resolver 自动兼容）
 *
 * **响应约定**：方法直接 return 业务 payload；全局 TransformInterceptor 会包一层
 * `{ success: true, data: ... }`。**不要在 controller 里手工包**，否则双包装坑见
 * .learnings/2026-05-14-transform-interceptor-double-wrap.md
 */
@ApiTags('Internal App Platform')
@ApiBearerAuth()
@Controller('internal-apps')
export class InternalAppPlatformController {
  private readonly logger = new Logger(InternalAppPlatformController.name);

  constructor(
    private readonly platformSvc: InternalAppPlatformService,
    private readonly tokenSvc: InternalAppTokenService,
    private readonly prisma: PrismaService,
    private readonly eventsSvc: InternalAppEventsService,
  ) {}

  /** 共用：拿 employeeSlug，没有就抛 401 */
  private async requireEmployeeSlug(req: {
    user?: { id?: string; userId?: string; employeeSlug?: string };
  }): Promise<string> {
    const userId = req.user?.id ?? req.user?.userId;
    if (!userId) {
      throw new UnauthorizedException({ code: 'unauthenticated' });
    }
    const employeeSlug = await this.platformSvc.resolveEmployeeSlug(req.user!);
    if (!employeeSlug) {
      throw new UnauthorizedException({
        code: 'no_binding',
        message:
          '当前用户未创建员工绑定，请先调 POST /tokens 完成首次接入（自动建 binding）',
      });
    }
    return employeeSlug;
  }

  @Post('tokens')
  @Auditable()
  @Sensitive()
  @HttpCode(HttpStatus.OK)
  @ApiOperation({ summary: '颁发 employee bearer token（首次同时建 binding）' })
  async issueToken(
    @Request()
    req: {
      user?: {
        id?: string;
        userId?: string;
        email?: string;
        username?: string;
        mailNickname?: string;
        organizationId?: string;
        currentOrganizationId?: string;
      };
    },
  ) {
    const user = req.user;
    const userId = user?.id ?? user?.userId;
    if (!userId) throw new UnauthorizedException({ code: 'unauthenticated' });

    const mailNickname =
      user?.mailNickname ??
      (user?.email ? user.email.split('@')[0] : null) ??
      user?.username ??
      null;
    if (!mailNickname) {
      throw new BadRequestException({
        code: 'no_mail_nickname',
        message: '无法确定 mailNickname（user.email 缺失）',
      });
    }

    let organizationId = user?.organizationId ?? user?.currentOrganizationId;
    if (!organizationId) {
      const role = await this.prisma.userRole.findFirst({
        where: { userId },
        select: { organizationId: true },
      });
      organizationId = role?.organizationId ?? undefined;
    }
    if (!organizationId) {
      throw new BadRequestException({
        code: 'no_organization',
        message: '当前用户不属于任何 organization',
      });
    }

    return this.tokenSvc.issue({ userId, mailNickname, organizationId });
  }

  @Post('tokens/revoke')
  @Auditable()
  @Sensitive()
  @HttpCode(HttpStatus.OK)
  @ApiOperation({ summary: '撤销当前 ACTIVE token（强确认 confirmText=REVOKE）' })
  async revokeToken(
    @Request() req: { user?: { id?: string; userId?: string; employeeSlug?: string } },
    @Body() body: { confirmText?: string },
  ) {
    if (body?.confirmText !== 'REVOKE') {
      throw new BadRequestException({
        code: 'invalid_confirm_text',
        message: '请在 confirmText 字段输入 "REVOKE" 以确认撤销',
      });
    }
    const employeeSlug = await this.requireEmployeeSlug(req);
    const result = await this.tokenSvc.revokeCurrent(employeeSlug);
    return { revokedAt: result.revokedAt?.toISOString() ?? null };
  }

  @Get('tokens/me')
  @ApiOperation({ summary: '查我的 token 状态（首次访问 no_binding 时正常返 hasToken=false）' })
  async getMyTokenStatus(
    @Request() req: { user?: { id?: string; userId?: string; employeeSlug?: string } },
  ) {
    // 不走 requireEmployeeSlug——首次访问员工还没建 binding，要返"没 token"状态
    // 让前端引导员工点 "生成新 token" 完成首次接入，而不是直接报 401。
    const userId = req.user?.id ?? req.user?.userId;
    if (!userId) {
      throw new UnauthorizedException({ code: 'unauthenticated' });
    }
    const employeeSlug = await this.platformSvc.resolveEmployeeSlug(req.user!);
    if (!employeeSlug) {
      // 未建 binding == 从未颁发过 token
      return {
        hasToken: false,
        status: null,
        prefix: null,
        issuedAt: null,
        expiresAt: null,
        expiringInDays: null,
        lastUsedAt: null,
      };
    }
    return this.tokenSvc.getMyTokenStatus(employeeSlug);
  }

  @Get('me/apps')
  @ApiOperation({ summary: '列出我的 app' })
  async listMyApps(
    @Request() req: { user?: { id?: string; userId?: string; employeeSlug?: string } },
    @Query('includeDestroyed') includeDestroyed?: string,
  ) {
    const employeeSlug = await this.requireEmployeeSlug(req);
    const apps = await this.platformSvc.listMyApps(
      employeeSlug,
      includeDestroyed === 'true',
    );
    return { apps };
  }

  @Get('me/events')
  @ApiOperation({ summary: '查我的活动事件流（07-api §4.5）' })
  async listMyEvents(
    @Request() req: { user?: { id?: string; userId?: string; organizationId?: string; currentOrganizationId?: string } },
    @Query('appId') appId?: string,
    @Query('eventType') eventType?: string,
    @Query('from') from?: string,
    @Query('to') to?: string,
    @Query('page') page?: string,
    @Query('pageSize') pageSize?: string,
  ) {
    const employeeSlug = await this.requireEmployeeSlug(req);
    // organizationId 优先从 JWT 拿；JWT 没带时从 binding 表反查（首次接入 binding 已存）。
    let organizationId =
      req.user?.organizationId ?? req.user?.currentOrganizationId;
    if (!organizationId) {
      const binding = await this.prisma.employeeSlugBinding.findUnique({
        where: { employeeSlug },
        select: { organizationId: true },
      });
      organizationId = binding?.organizationId;
    }
    if (!organizationId) {
      throw new UnauthorizedException({ code: 'no_organization' });
    }
    const result = await this.eventsSvc.list({
      organizationId,
      employeeSlug,
      appId: appId?.trim() || undefined,
      eventTypes: eventType
        ? eventType.split(',').map((s) => s.trim()).filter(Boolean)
        : undefined,
      from: parseIsoOrUndefined(from),
      to: parseIsoOrUndefined(to),
      page: parsePositiveInt(page),
      pageSize: parsePositiveInt(pageSize),
    });
    return {
      ...result,
      // 员工只看 ADMIN 操作的 actorEmail（联系 IT 用）；OWNER 事件 actor 就是自己，前端无需展示
      items: result.items.map((it) => ({
        ...it,
        actorEmail: it.actorRole === 'ADMIN' ? it.actorEmail : null,
      })),
    };
  }
}

