import {
  Controller,
  Post,
  Body,
  Logger,
  ForbiddenException,
  HttpException,
  HttpStatus,
  NotFoundException,
  Inject,
  UseGuards,
} from '@nestjs/common';
import type { AgentSessionEvent } from '@mariozechner/pi-coding-agent';
import { Throttle } from '@nestjs/throttler';
import { AgentService } from '../agent/agent.service.js';
import { AuthGuard } from '../auth/auth.guard.js';
import { CurrentUser } from '../auth/current-user.decorator.js';
import { scopeFromUser, type AuthenticatedUserLike } from '../auth/session-scope.js';
import { v4 as uuid } from 'uuid';
import { ChatRequestDto } from './chat.dto.js';

interface ChatResponse {
  conversationId: string;
  text: string;
}

@Controller('api/chat')
@UseGuards(AuthGuard)
export class ChatController {
  private readonly logger = new Logger(ChatController.name);

  constructor(@Inject(AgentService) private readonly agentService: AgentService) {}

  @Post()
  @Throttle({ default: { limit: 10, ttl: 60_000 } })
  async chat(
    @Body() body: ChatRequestDto,
    @CurrentUser() user: AuthenticatedUserLike,
  ): Promise<ChatResponse> {
    const conversationId = body.conversationId ?? uuid();
    const scope = scopeFromUser(user);

    try {
      let agentSession = this.agentService.getSession(conversationId, scope);
      if (!agentSession) {
        agentSession = await this.agentService.createSession(conversationId, {
          userId: scope.userId,
          tenantId: scope.tenantId,
        });
      }
    } catch (err) {
      if (err instanceof ForbiddenException) {
        throw new NotFoundException('Session not found');
      }
      this.logger.error(
        `Session creation failed for conversation=${conversationId}`,
        err instanceof Error ? err.stack : String(err),
      );
      throw new HttpException('Agent session unavailable', HttpStatus.SERVICE_UNAVAILABLE);
    }

    this.logger.debug(`Handling chat request for user=${user.id}, conversation=${conversationId}`);

    let responseText = '';

    const done = new Promise<void>((resolve, reject) => {
      const timer = setTimeout(() => {
        cleanup();
        this.logger.error(`Agent response timed out after 120s for conversation=${conversationId}`);
        reject(new Error('Agent response timed out'));
      }, 120_000);

      const cleanup = this.agentService.onEvent(
        conversationId,
        (event: AgentSessionEvent) => {
          if (
            event.type === 'message_update' &&
            event.assistantMessageEvent.type === 'text_delta'
          ) {
            responseText += event.assistantMessageEvent.delta;
          }
          if (event.type === 'agent_end') {
            clearTimeout(timer);
            cleanup();
            resolve();
          }
        },
        scope,
      );
    });

    try {
      await this.agentService.prompt(conversationId, body.content, scope);
      await done;
    } catch (err) {
      if (err instanceof HttpException) throw err;
      const message = err instanceof Error ? err.message : String(err);
      if (message.includes('timed out')) {
        throw new HttpException('Agent response timed out', HttpStatus.GATEWAY_TIMEOUT);
      }
      this.logger.error(`Chat prompt failed for conversation=${conversationId}`, String(err));
      throw new HttpException('Agent processing failed', HttpStatus.INTERNAL_SERVER_ERROR);
    }

    return { conversationId, text: responseText };
  }
}
