import {
  Body,
  Controller,
  ForbiddenException,
  Get,
  Inject,
  InternalServerErrorException,
  Post,
} from '@nestjs/common';
import { randomBytes, createHash } from 'node:crypto';
import { count, eq, type Db, users as usersTable, adminTokens } from '@mosaicstack/db';
import type { Auth } from '@mosaicstack/auth';
import { v4 as uuid } from 'uuid';
import { AUTH } from '../auth/auth.tokens.js';
import { DB } from '../database/database.module.js';
import { BootstrapSetupDto } from './bootstrap.dto.js';
import type { BootstrapStatusDto, BootstrapResultDto } from './bootstrap.dto.js';

@Controller('api/bootstrap')
export class BootstrapController {
  constructor(
    @Inject(AUTH) private readonly auth: Auth,
    @Inject(DB) private readonly db: Db,
  ) {}

  @Get('status')
  async status(): Promise<BootstrapStatusDto> {
    const [result] = await this.db.select({ total: count() }).from(usersTable);
    return { needsSetup: (result?.total ?? 0) === 0 };
  }

  @Post('setup')
  async setup(@Body() dto: BootstrapSetupDto): Promise<BootstrapResultDto> {
    // Only allow setup when zero users exist
    const [result] = await this.db.select({ total: count() }).from(usersTable);
    if ((result?.total ?? 0) > 0) {
      throw new ForbiddenException('Setup already completed — users exist');
    }

    // Create admin user via BetterAuth API
    const authApi = this.auth.api as unknown as {
      createUser: (opts: {
        body: { name: string; email: string; password: string; role?: string };
      }) => Promise<{
        user: { id: string; name: string; email: string };
      }>;
    };

    const created = await authApi.createUser({
      body: {
        name: dto.name,
        email: dto.email,
        password: dto.password,
        role: 'admin',
      },
    });

    // Verify user was created
    const [user] = await this.db
      .select()
      .from(usersTable)
      .where(eq(usersTable.id, created.user.id))
      .limit(1);

    if (!user) throw new InternalServerErrorException('User created but not found');

    // Ensure role is admin (createUser may not set it via BetterAuth)
    if (user.role !== 'admin') {
      await this.db.update(usersTable).set({ role: 'admin' }).where(eq(usersTable.id, user.id));
    }

    // Generate admin API token
    const plaintext = randomBytes(32).toString('hex');
    const tokenHash = createHash('sha256').update(plaintext).digest('hex');
    const tokenId = uuid();

    const [token] = await this.db
      .insert(adminTokens)
      .values({
        id: tokenId,
        userId: user.id,
        tokenHash,
        label: 'Initial setup token',
        scope: 'admin',
      })
      .returning();

    return {
      user: {
        id: user.id,
        name: user.name,
        email: user.email,
        role: 'admin',
      },
      token: {
        id: token!.id,
        plaintext,
        label: token!.label,
      },
    };
  }
}
