import {
  Controller,
  Get,
  Delete,
  Query,
  Req,
  Res,
  Logger,
  Param,
} from '@nestjs/common';
import { Request } from 'express';
import { ApiTags, ApiOperation } from '@nestjs/swagger';
import { ConfigService } from '@nestjs/config';
import { Response } from 'express';
import { v4 as uuidv4 } from 'uuid';
import { Public } from '../../common/decorators/public.decorator';
import { CurrentUser } from '../../common/decorators/current-user.decorator';
import { Roles } from '../../common/decorators/roles.decorator';
import { RolesGuard } from '../../common/guards/roles.guard';
import { RedisService } from '../../redis/redis.service';
import { EntraIdService } from '../auth/sso/entra-id.service';
import { IntegrationsService } from './integrations.service';

interface JwtUser {
  sub: string;
  email: string;
  role: string;
}

/**
 * IntegrationsController
 *
 * Verwaltet Drittanbieter-OAuth-Verbindungen pro User.
 * Derzeit: Microsoft 365 (Graph API Scopes).
 *
 * Routen:
 * - GET  /auth/integrations/microsoft-365            → OAuth-Flow starten
 * - GET  /auth/integrations/microsoft-365/callback   → OAuth-Callback
 * - GET  /users/me/integrations                      → Liste der Verbindungen
 * - DELETE /users/me/integrations/microsoft-365      → Verbindung trennen
 * - GET  /integrations/:userId/microsoft-365/token   → Intern: Token fuer CRM-Service
 */
@ApiTags('Integrations')
@Controller()
export class IntegrationsController {
  private readonly logger = new Logger(IntegrationsController.name);

  constructor(
    private readonly integrationsService: IntegrationsService,
    private readonly entraIdService: EntraIdService,
    private readonly redis: RedisService,
    private readonly config: ConfigService,
  ) {}

  /**
   * GET /api/v1/auth/integrations/microsoft-365
   * OAuth2-Flow fuer Microsoft 365 vorbereiten.
   * Gibt die Microsoft-Authorisierungs-URL als JSON zurueck.
   * Das Frontend leitet den Browser dann client-seitig weiter (axios + window.location).
   */
  @Get('auth/integrations/microsoft-365')
  @ApiOperation({ summary: 'Microsoft 365 Integration starten' })
  async initM365Integration(
    @CurrentUser() user: JwtUser,
    @Req() req: Request,
  ): Promise<{ success: boolean; data: { url: string } }> {
    // Redirect-URI dynamisch aus dem Anfrage-Host ableiten
    // Protokoll: aus der konfigurierten SSO-URI (HTTPS) — Azure akzeptiert nur HTTPS
    // fuer nicht-localhost URIs. x-forwarded-proto koennte http sein (Traefik-intern).
    const host = (req.get('x-forwarded-host') || req.get('host') || '').split(',')[0].trim();
    const configuredUri = this.entraIdService.getIntegrationRedirectUri();
    let proto = 'https';
    try {
      proto = new URL(configuredUri).protocol.replace(':', '');
    } catch {
      proto = req.get('x-forwarded-proto') || 'https';
    }
    const redirectUri = host
      ? `${proto}://${host}/api/v1/auth/integrations/microsoft-365/callback`
      : undefined;

    // State = UUID, in Redis hinterlegen: {userId, redirectUri} — 5 Min TTL
    const state = uuidv4();
    await this.redis.set(
      `integration_state:${state}`,
      JSON.stringify({ userId: user.sub, redirectUri: redirectUri ?? '' }),
      300,
    );

    const authUrl = await this.entraIdService.getIntegrationAuthUrl(state, redirectUri);
    this.logger.log(
      `M365-Integration OAuth-Flow gestartet fuer User ${user.sub} (redirectUri: ${redirectUri ?? 'default'})`,
    );
    return { success: true, data: { url: authUrl } };
  }

  /**
   * GET /api/v1/auth/integrations/microsoft-365/callback
   * Callback von Microsoft — Token speichern, redirect zum Profil.
   */
  @Get('auth/integrations/microsoft-365/callback')
  @Public()
  @ApiOperation({ summary: 'Microsoft 365 Integration Callback' })
  async m365Callback(
    @Query('code') code: string,
    @Query('state') state: string,
    @Query('error') error: string,
    @Query('error_description') errorDescription: string,
    @Res() res: Response,
  ): Promise<void> {
    const frontendUrl = this.config.get<string>(
      'FRONTEND_URL',
      'http://172.20.10.59',
    );
    const successUrl = `${frontendUrl}/profile?integration=microsoft-365&status=success`;
    const errorBase = `${frontendUrl}/profile?integration=microsoft-365&status=error`;

    if (error) {
      this.logger.warn(`M365-Integration Fehler: ${error} — ${errorDescription}`);
      res.redirect(`${errorBase}&message=${encodeURIComponent(errorDescription || error)}`);
      return;
    }

    if (!code || !state) {
      res.redirect(`${errorBase}&message=Ungueltige+Antwort`);
      return;
    }

    // State aus Redis laden (enthaelt {userId, redirectUri})
    const rawState = await this.redis.get(`integration_state:${state}`);
    if (!rawState) {
      this.logger.warn('M365-Integration State ungueltig oder abgelaufen');
      res.redirect(`${errorBase}&message=Sitzung+abgelaufen`);
      return;
    }
    await this.redis.del(`integration_state:${state}`);

    let userId: string;
    let savedRedirectUri: string | undefined;
    try {
      const parsed = JSON.parse(rawState) as { userId: string; redirectUri?: string };
      userId = parsed.userId;
      savedRedirectUri = parsed.redirectUri || undefined;
    } catch {
      // Fallback: alter Format (nur userId als String)
      userId = rawState;
    }

    try {
      const tokenResult = await this.entraIdService.handleIntegrationCallback(code, savedRedirectUri);
      await this.integrationsService.saveM365Integration(userId, tokenResult);
      this.logger.log(`M365-Integration erfolgreich fuer User ${userId}`);
      res.redirect(successUrl);
    } catch (err) {
      this.logger.error(
        `M365-Integration Callback Fehler: ${(err as Error).message}`,
      );
      res.redirect(
        `${errorBase}&message=${encodeURIComponent('Verbindung fehlgeschlagen — bitte erneut versuchen')}`,
      );
    }
  }

  /**
   * GET /api/v1/users/me/integrations
   * Liste aller Drittanbieter-Verbindungen des eingeloggten Users.
   */
  @Get('users/me/integrations')
  @ApiOperation({ summary: 'Eigene Integrationen auflisten' })
  async listMyIntegrations(@CurrentUser() user: JwtUser) {
    const integrations = await this.integrationsService.listIntegrations(user.sub);
    return { success: true, data: integrations };
  }

  /**
   * DELETE /api/v1/users/me/integrations/microsoft-365
   * Microsoft 365 Integration trennen.
   */
  @Delete('users/me/integrations/microsoft-365')
  @ApiOperation({ summary: 'Microsoft 365 Integration trennen' })
  async disconnectM365(@CurrentUser() user: JwtUser) {
    await this.integrationsService.removeM365Integration(user.sub);
    return { success: true, message: 'Microsoft 365 Verbindung getrennt' };
  }

  /**
   * GET /api/v1/integrations/:userId/microsoft-365/token
   * INTERNER ENDPOINT fuer CRM-Service.
   * Gibt den aktuellen M365-Access-Token zurueck (refreshed automatisch).
   * Zugriff: nur PLATFORM_ADMIN oder eigener User.
   */
  @Get('integrations/:userId/microsoft-365/token')
  @ApiOperation({ summary: 'M365 Token fuer internen Dienst (CRM-Service)' })
  async getTokenForService(
    @Param('userId') userId: string,
    @CurrentUser() caller: JwtUser,
  ) {
    // Nur PLATFORM_ADMIN oder der User selbst darf diesen Endpoint nutzen
    if (caller.role !== 'PLATFORM_ADMIN' && caller.sub !== userId) {
      return { success: false, error: { message: 'Kein Zugriff' } };
    }

    const token = await this.integrationsService.getM365Token(userId);
    return { success: true, data: token };
  }

  /**
   * GET /api/v1/users/me/integrations/microsoft-365/token
   * Eigenen M365-Token abrufen (fuer CRM-Service der den User-JWT weiterleitet).
   */
  @Get('users/me/integrations/microsoft-365/token')
  @ApiOperation({ summary: 'Eigenen M365 Token abrufen' })
  async getMyM365Token(@CurrentUser() user: JwtUser) {
    const token = await this.integrationsService.getM365Token(user.sub);
    return { success: true, data: token };
  }
}
