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

export interface DeviceInfo {
  id: string;
  deviceFingerprint: string;
  platform?: string;
  trusted: boolean;
  lastSeen: Date;
  isCurrent?: boolean;
}

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

  constructor(
    @InjectRepository(DeviceRegistry)
    private deviceRepository: Repository<DeviceRegistry>,
  ) {}

  /**
   * Get all devices for a user
   */
  async getUserDevices(
    userId: string,
    currentDeviceId?: string,
  ): Promise<DeviceInfo[]> {
    const devices = await this.deviceRepository.find({
      where: {
        user: { id: userId },
      },
      order: {
        lastSeen: 'DESC',
      },
    });

    return devices.map((device) => ({
      id: device.id,
      deviceFingerprint: device.deviceFingerprint,
      platform: device.platform,
      trusted: device.trusted,
      lastSeen: device.lastSeen,
      isCurrent: device.id === currentDeviceId,
    }));
  }

  /**
   * Get trusted devices only
   */
  async getTrustedDevices(userId: string): Promise<DeviceInfo[]> {
    const devices = await this.deviceRepository.find({
      where: {
        user: { id: userId },
        trusted: true,
      },
      order: {
        lastSeen: 'DESC',
      },
    });

    return devices.map((device) => ({
      id: device.id,
      deviceFingerprint: device.deviceFingerprint,
      platform: device.platform,
      trusted: device.trusted,
      lastSeen: device.lastSeen,
    }));
  }

  /**
   * Trust a device
   */
  async trustDevice(
    userId: string,
    deviceId: string,
  ): Promise<{ success: boolean; message: string }> {
    const device = await this.deviceRepository.findOne({
      where: {
        id: deviceId,
        user: { id: userId },
      },
    });

    if (!device) {
      throw new NotFoundException('Device not found');
    }

    if (device.trusted) {
      return {
        success: true,
        message: 'Device is already trusted',
      };
    }

    await this.deviceRepository.update(deviceId, {
      trusted: true,
    });

    this.logger.log(`Device ${deviceId} marked as trusted for user ${userId}`);

    return {
      success: true,
      message: 'Device marked as trusted',
    };
  }

  /**
   * Revoke trust from a device
   */
  async revokeDeviceTrust(
    userId: string,
    deviceId: string,
    currentDeviceId?: string,
  ): Promise<{ success: boolean; message: string }> {
    const device = await this.deviceRepository.findOne({
      where: {
        id: deviceId,
        user: { id: userId },
      },
    });

    if (!device) {
      throw new NotFoundException('Device not found');
    }

    // Warn if revoking current device
    if (currentDeviceId && device.id === currentDeviceId) {
      this.logger.warn(
        `User ${userId} is revoking trust from their current device ${deviceId}`,
      );
    }

    await this.deviceRepository.update(deviceId, {
      trusted: false,
    });

    this.logger.log(
      `Device trust revoked for device ${deviceId}, user ${userId}`,
    );

    return {
      success: true,
      message: 'Device trust revoked',
    };
  }

  /**
   * Remove a device completely
   */
  async removeDevice(
    userId: string,
    deviceId: string,
    currentDeviceId?: string,
  ): Promise<{ success: boolean; message: string }> {
    const device = await this.deviceRepository.findOne({
      where: {
        id: deviceId,
        user: { id: userId },
      },
    });

    if (!device) {
      throw new NotFoundException('Device not found');
    }

    // Don't allow removing current device
    if (currentDeviceId && device.id === currentDeviceId) {
      throw new BadRequestException(
        'Cannot remove current device. Please use a different device to remove this one.',
      );
    }

    // Note: This will cascade delete all refresh tokens associated with this device
    await this.deviceRepository.delete(deviceId);

    this.logger.log(`Device ${deviceId} removed for user ${userId}`);

    return {
      success: true,
      message: 'Device removed successfully',
    };
  }

  /**
   * Remove all devices except current
   */
  async removeAllDevices(
    userId: string,
    currentDeviceId?: string,
  ): Promise<{ success: boolean; message: string; removedCount: number }> {
    const queryBuilder = this.deviceRepository
      .createQueryBuilder('device')
      .where('device.userId = :userId', { userId });

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

    const devicesToRemove = await queryBuilder.getMany();
    const removedCount = devicesToRemove.length;

    if (removedCount === 0) {
      return {
        success: true,
        message: 'No other devices to remove',
        removedCount: 0,
      };
    }

    const deviceIds = devicesToRemove.map((d) => d.id);
    await this.deviceRepository.delete(deviceIds);

    this.logger.log(
      `Removed ${removedCount} devices for user ${userId}, kept current device`,
    );

    return {
      success: true,
      message: `Successfully removed ${removedCount} device(s)`,
      removedCount,
    };
  }

  /**
   * Get device count for a user
   */
  async getDeviceCount(userId: string): Promise<{
    total: number;
    trusted: number;
    untrusted: number;
  }> {
    const allDevices = await this.deviceRepository.find({
      where: {
        user: { id: userId },
      },
    });

    const trusted = allDevices.filter((d) => d.trusted).length;
    const untrusted = allDevices.filter((d) => !d.trusted).length;

    return {
      total: allDevices.length,
      trusted,
      untrusted,
    };
  }

  /**
   * Check if a device is trusted
   */
  async isDeviceTrusted(userId: string, deviceId: string): Promise<boolean> {
    const device = await this.deviceRepository.findOne({
      where: {
        id: deviceId,
        user: { id: userId },
      },
    });

    return device?.trusted || false;
  }
}
