import {
  Injectable,
  NotFoundException,
  UnauthorizedException,
  Logger,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { RefreshToken } from '../../database/entities';

export interface SessionInfo {
  id: string;
  deviceFingerprint: string;
  platform?: string;
  lastUsed: Date;
  createdAt: Date;
  ipAddress?: string;
  isCurrent: boolean;
}

@Injectable()
export class SessionService {
  private readonly logger = new Logger(SessionService.name);

  constructor(
    @InjectRepository(RefreshToken)
    private refreshTokenRepository: Repository<RefreshToken>,
  ) {}

  /**
   * Get all active sessions for a user
   */
  async getUserSessions(
    userId: string,
    currentTokenId?: string,
  ): Promise<SessionInfo[]> {
    const sessions = await this.refreshTokenRepository.find({
      where: {
        user: { id: userId },
        revoked: false,
      },
      relations: ['device'],
      order: {
        createdAt: 'DESC',
      },
    });

    return sessions.map((session) => ({
      id: session.id,
      deviceFingerprint: session.device?.deviceFingerprint || 'unknown',
      platform: session.device?.platform,
      lastUsed: session.device?.lastSeen || session.createdAt,
      createdAt: session.createdAt,
      ipAddress: undefined, // IP not stored on RefreshToken
      isCurrent: session.id === currentTokenId,
    }));
  }

  /**
   * Revoke a specific session
   */
  async revokeSession(
    userId: string,
    sessionId: string,
    currentTokenId?: string,
  ): Promise<{ success: boolean; message: string }> {
    // Find the session
    const session = await this.refreshTokenRepository.findOne({
      where: {
        id: sessionId,
        user: { id: userId },
      },
    });

    if (!session) {
      throw new NotFoundException('Session not found');
    }

    // Don't allow revoking the current session through this endpoint
    if (currentTokenId && session.id === currentTokenId) {
      throw new UnauthorizedException(
        'Cannot revoke current session. Use logout endpoint instead.',
      );
    }

    // Revoke the session
    await this.refreshTokenRepository.update(sessionId, {
      revoked: true,
      revokedAt: new Date(),
    });

    this.logger.log(`Session ${sessionId} revoked for user ${userId}`);

    return {
      success: true,
      message: 'Session revoked successfully',
    };
  }

  /**
   * Revoke all sessions except the current one
   */
  async revokeAllSessions(
    userId: string,
    currentTokenId?: string,
  ): Promise<{ success: boolean; message: string; revokedCount: number }> {
    // Build query to find all sessions except current
    const queryBuilder = this.refreshTokenRepository
      .createQueryBuilder('token')
      .where('token.userId = :userId', { userId })
      .andWhere('token.revoked = :revoked', { revoked: false });

    // Exclude current token if provided
    if (currentTokenId) {
      queryBuilder.andWhere('token.id != :currentTokenId', { currentTokenId });
    }

    const sessionsToRevoke = await queryBuilder.getMany();
    const revokedCount = sessionsToRevoke.length;

    if (revokedCount === 0) {
      return {
        success: true,
        message: 'No other active sessions to revoke',
        revokedCount: 0,
      };
    }

    // Revoke all sessions
    const sessionIds = sessionsToRevoke.map((s) => s.id);
    await this.refreshTokenRepository.update(sessionIds, {
      revoked: true,
      revokedAt: new Date(),
    });

    this.logger.log(
      `Revoked ${revokedCount} sessions for user ${userId}, kept current session`,
    );

    return {
      success: true,
      message: `Successfully revoked ${revokedCount} session(s)`,
      revokedCount,
    };
  }

  /**
   * Get session count for a user
   */
  async getActiveSessionCount(userId: string): Promise<number> {
    return await this.refreshTokenRepository.count({
      where: {
        user: { id: userId },
        revoked: false,
      },
    });
  }

  /**
   * Check if a session is still valid
   */
  async isSessionValid(sessionId: string, userId: string): Promise<boolean> {
    const session = await this.refreshTokenRepository.findOne({
      where: {
        id: sessionId,
        user: { id: userId },
      },
    });

    if (!session) {
      return false;
    }

    // Check if revoked
    if (session.revoked) {
      return false;
    }

    // Check if expired
    if (session.expiresAt < new Date()) {
      return false;
    }

    return true;
  }
}
