import {
  Injectable,
  UnauthorizedException,
} from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Response } from 'express';
import { User } from '../user/user.entity';
import { SetSessionDto } from './dto/set-session.dto';

interface SuperOAuthUser {
  id: string;
  tenantId: string;
  email: string | null;
  nickname: string;
  isActive: boolean;
  linkedProviders: string[];
}

const COOKIE_NAME         = 'session';
const REFRESH_COOKIE_NAME = 'refresh_token';

@Injectable()
export class AuthService {
  private readonly superOauthUrl: string;

  constructor(
    @InjectRepository(User)
    private readonly userRepository: Repository<User>,
    private readonly configService: ConfigService,
  ) {
    this.superOauthUrl = this.configService.getOrThrow<string>('SUPER_OAUTH_URL');
  }

  async setSession(dto: SetSessionDto, res: Response): Promise<Omit<User, 'superOauthId'>> {
    const oauthUser = await this.introspectToken(dto.token);

    // Upsert user
    let user = await this.userRepository.findOne({
      where: { superOauthId: oauthUser.id },
    });

    if (!user) {
      user = this.userRepository.create({
        superOauthId: oauthUser.id,
        username: oauthUser.nickname,
        email: oauthUser.email,
      });
    } else {
      user.username = oauthUser.nickname;
      user.email = oauthUser.email;
    }

    await this.userRepository.save(user);

    // Cookie httpOnly — session = UUID interne
    const isProduction = this.configService.get('NODE_ENV') === 'production';
    res.cookie(COOKIE_NAME, user.id, {
      httpOnly: true,
      signed: true,
      secure: isProduction,
      sameSite: 'lax',
      maxAge: 7 * 24 * 60 * 60 * 1000, // 7 jours
    });

    // Refresh token cookie si fourni
    if (dto.refreshToken) {
      res.cookie(REFRESH_COOKIE_NAME, dto.refreshToken, {
        httpOnly: true,
        signed: true,
        secure: isProduction,
        sameSite: 'lax',
        maxAge: 30 * 24 * 60 * 60 * 1000, // 30 jours
      });
    }

    const { superOauthId: _, ...safeUser } = user;
    return safeUser;
  }

  async refreshSession(res: Response, refreshToken: string): Promise<{ success: boolean }> {
    // Exchange refresh token for new access token via SuperOAuth
    const response = await fetch(`${this.superOauthUrl}/oauth/token`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
      body: new URLSearchParams({
        grant_type: 'refresh_token',
        refresh_token: refreshToken,
      }).toString(),
    });

    if (!response.ok) {
      throw new UnauthorizedException('Refresh token invalide ou expiré');
    }

    const data = await response.json();
    if (!data.access_token) {
      throw new UnauthorizedException('Refresh échoué — pas de token');
    }

    // Validate the new access token to get user data
    const oauthUser = await this.introspectToken(data.access_token);

    const user = await this.userRepository.findOne({
      where: { superOauthId: oauthUser.id },
    });

    if (!user) {
      throw new UnauthorizedException('Utilisateur introuvable après refresh');
    }

    // Set new cookies
    const isProduction = this.configService.get('NODE_ENV') === 'production';
    res.cookie(COOKIE_NAME, user.id, {
      httpOnly: true,
      signed: true,
      secure: isProduction,
      sameSite: 'lax',
      maxAge: 7 * 24 * 60 * 60 * 1000,
    });

    if (data.refresh_token) {
      res.cookie(REFRESH_COOKIE_NAME, data.refresh_token, {
        httpOnly: true,
        signed: true,
        secure: isProduction,
        sameSite: 'lax',
        maxAge: 30 * 24 * 60 * 60 * 1000,
      });
    }

    return { success: true };
  }

  async getMe(user: User): Promise<Omit<User, 'superOauthId'>> {
    const { superOauthId: _, ...safeUser } = user;
    return safeUser;
  }

  logout(res: Response): void {
    res.clearCookie(COOKIE_NAME);
    res.clearCookie(REFRESH_COOKIE_NAME);
  }

  private async introspectToken(token: string): Promise<SuperOAuthUser> {
    const response = await fetch(`${this.superOauthUrl}/api/v1/auth/token/validate`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ token }),
    });

    if (!response.ok) {
      throw new UnauthorizedException('Token SuperOAuth invalide');
    }

    const data = await response.json();
    if (!data.data?.valid || !data.data.user) {
      throw new UnauthorizedException('Token SuperOAuth invalide ou expiré');
    }

    if (!data.data.user.isActive) {
      throw new UnauthorizedException('Compte SuperOAuth désactivé');
    }

    return data.data.user as SuperOAuthUser;
  }
}
