import {
  Body,
  Controller,
  Delete,
  Get,
  HttpCode,
  Param,
  Patch,
  Post,
  Req,
  UseGuards,
} from '@nestjs/common'
import { ActorService } from '../auth/actor.service.js'
import { clientIp } from '../auth/client-ip.js'
import { CurrentUser } from '../auth/current-user.decorator.js'
import { JwtAuthGuard } from '../auth/jwt-auth.guard.js'
import { OperatorGuard } from '../auth/operator.guard.js'
import type { AuthentikJwtPayload } from '../auth/jwt-payload.interface.js'
import type { AuditActor } from '../audit/audit.service.js'
import { InvitePartnerUserDto } from '../users/dto/invite-partner-user.dto.js'
import { UsersService } from '../users/users.service.js'
import { CreatePartnerDto } from './dto/create-partner.dto.js'
import { UpdatePartnerDto } from './dto/update-partner.dto.js'
import { PartnersService } from './partners.service.js'

function auditActor(
  user: { _id: unknown; email: string },
  req: Parameters<typeof clientIp>[0],
): AuditActor {
  return {
    userId: String(user._id),
    email: user.email,
    ip: clientIp(req),
  }
}

// Partners are operator-managed only. Every endpoint requires an
// operator-scoped token (aud === 'dezky-operator') plus platformAdmin on the
// resolved user. A self-serve partner portal (partner.dezky.local) is a
// future surface and will hit different endpoints scoped to "this partner's
// own customers" rather than the full set.
@Controller('partners')
@UseGuards(JwtAuthGuard, OperatorGuard)
export class PartnersController {
  constructor(
    private readonly partners: PartnersService,
    private readonly users: UsersService,
    private readonly actorService: ActorService,
  ) {}

  @Post()
  async create(
    @Body() dto: CreatePartnerDto,
    @CurrentUser() jwt: AuthentikJwtPayload,
    @Req() req: Parameters<typeof clientIp>[0],
  ) {
    const user = await this.actorService.resolve(jwt)
    return this.partners.create(dto, auditActor(user, req))
  }

  @Get()
  async findAll() {
    const rows = await this.partners.findAllWithStats()
    return rows.map((r) => ({ ...r.partner.toObject(), customers: r.customers }))
  }

  @Get(':slug')
  async findOne(@Param('slug') slug: string) {
    const row = await this.partners.findOneWithStats(slug)
    return { ...row.partner.toObject(), customers: row.customers }
  }

  @Get(':slug/tenants')
  listTenants(@Param('slug') slug: string) {
    return this.partners.listTenants(slug)
  }

  @Patch(':slug')
  async update(
    @Param('slug') slug: string,
    @Body() dto: UpdatePartnerDto,
    @CurrentUser() jwt: AuthentikJwtPayload,
    @Req() req: Parameters<typeof clientIp>[0],
  ) {
    const user = await this.actorService.resolve(jwt)
    return this.partners.update(slug, dto, auditActor(user, req))
  }

  @Delete(':slug')
  @HttpCode(204)
  async terminate(
    @Param('slug') slug: string,
    @CurrentUser() jwt: AuthentikJwtPayload,
    @Req() req: Parameters<typeof clientIp>[0],
  ) {
    const user = await this.actorService.resolve(jwt)
    await this.partners.terminate(slug, auditActor(user, req))
  }

  // Partner-staff team listing. Returns the User docs whose partnerId matches
  // this partner. The /partners/:slug page's Team section calls this on load.
  @Get(':slug/users')
  async listUsers(@Param('slug') slug: string) {
    const partner = await this.partners.findOneBySlug(slug)
    return this.users.listPartnerUsers(partner._id)
  }

  // Invite a new partner-staff user. Resolves slug → partner, delegates to
  // UsersService.invitePartnerUser which handles Authentik user creation,
  // group assignment, local User pre-create, and audit recording.
  @Post(':slug/users')
  async inviteUser(
    @Param('slug') slug: string,
    @Body() dto: InvitePartnerUserDto,
    @CurrentUser() jwt: AuthentikJwtPayload,
    @Req() req: Parameters<typeof clientIp>[0],
  ) {
    const actor = await this.actorService.resolve(jwt)
    const partner = await this.partners.findOneBySlug(slug)
    return this.users.invitePartnerUser(
      dto,
      { _id: partner._id, slug: partner.slug },
      auditActor(actor, req),
    )
  }
}
