import { Body, Controller, Get, Post, Query, Req } from '@nestjs/common';
import type { Request } from 'express';
import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger';
import { ZodResponse } from 'nestjs-zod';
import { Permission } from '@pac/types';
import type { AccessTokenPayload } from '@pac/types';
import { CurrentUser } from '../../common/decorators/current-user.decorator';
import { RequirePermission } from '../../common/decorators/permissions.decorator';
import {
  TenantScope,
  TenantScopeContext,
} from '../../common/decorators/tenant-scope.decorator';
import { ActivityService } from './activity.service';
import {
  ActivityStatsResponseDto,
  TrackActivityRequestDto,
  TrackActivityResponseDto,
} from './dto/activity.dto';

/**
 * 行为埋点 —— POST /pac/v1/activities(上报) / GET /pac/v1/activities/stats(看数)
 *
 * ⛔ 上报接口**不接受身份字段**:userId / role / orgScope 全部从 JWT 取(见 service 注释)。
 *    请求体是 strictObject,前端多传一个 userId 会被 Zod 直接拒掉 —— 这是故意的。
 * ⚠️ 上报**不挂权限**:任何登录用户都能给自己记行为(记的就是他自己)。
 *    看数才要 PLATFORM_MANAGE。
 */
@ApiTags('activity')
@ApiBearerAuth('accessToken')
@Controller('activities')
export class ActivityController {
  constructor(private readonly svc: ActivityService) {}

  @Post()
  @ZodResponse({ status: 200, type: TrackActivityResponseDto })
  @ApiOperation({ summary: '上报行为埋点(批量,身份取自 token)' })
  async track(
    @CurrentUser() user: AccessTokenPayload,
    @Body() dto: TrackActivityRequestDto,
    @Req() req: Request,
  ) {
    const accepted = await this.svc.track(user, dto.items, {
      hostOrigin: originOf(req),
      // 会话标识 = `sub:iat`。⚠️ access token **没有 jti**(只有 refresh token 有,
      //   见 auth.service 的 Redis 白名单),拿 jti 会得到一列恒空的死数据。
      //   `iat` 是这张票的签发秒 —— 同一次登录内所有行为共享它,正好把漏斗串起来。
      sessionId: user.iat ? `${user.sub}:${user.iat}` : null,
    });
    return { accepted };
  }

  @Get('stats')
  @RequirePermission(Permission.PLATFORM_MANAGE)
  @ZodResponse({ status: 200, type: ActivityStatsResponseDto })
  @ApiOperation({ summary: 'PV/UV + 漏斗:按 (天, surface, action) 聚合;默认近 30 天' })
  stats(
    @TenantScope() scope: TenantScopeContext,
    @Query('from') from?: string,
    @Query('to') to?: string,
    @Query('surface') surface?: string,
  ) {
    return this.svc.stats(scope, { from, to, surface });
  }
}

/// 宿主来源域:iframe 嵌入时浏览器必带 Origin;直开页面退到 Referer 的 origin。
/// 都没有就是 null(比如服务端脚本调的)。
function originOf(req: Request): string | null {
  const o = req.headers.origin;
  if (typeof o === 'string' && o) return o.slice(0, 200);
  const r = req.headers.referer;
  if (typeof r === 'string' && r) {
    try {
      return new URL(r).origin.slice(0, 200);
    } catch {
      return null;
    }
  }
  return null;
}
