import { Injectable, ServiceUnavailableException } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { ServerConnection } from '../../entities/server-connection.entity';
import { ServerConfig } from '../../entities/server-config.entity';
import { NotificationsConfig } from '../../entities/notifications-config.entity';
import { PublicSiteConfig } from '../../entities/public-site-config.entity';
import { AlertConfig } from '../../entities/alert-config.entity';
import { SetupServerDto } from './dto/setup-server.dto';
import * as crypto from 'crypto';

@Injectable()
export class SetupService {
  constructor(
    private readonly configService: ConfigService,
    @InjectRepository(ServerConnection)
    private readonly connectionRepo: Repository<ServerConnection>,
    @InjectRepository(ServerConfig)
    private readonly configRepo: Repository<ServerConfig>,
    @InjectRepository(NotificationsConfig)
    private readonly notifConfigRepo: Repository<NotificationsConfig>,
    @InjectRepository(PublicSiteConfig)
    private readonly publicSiteRepo: Repository<PublicSiteConfig>,
    @InjectRepository(AlertConfig)
    private readonly alertConfigRepo: Repository<AlertConfig>,
  ) {}

  async setupServer(licenseId: string, dto: SetupServerDto): Promise<ServerConnection> {
    // Check if connection already exists
    let connection = await this.connectionRepo.findOne({
      where: { license_id: licenseId },
    });

    if (!connection) {
      connection = this.connectionRepo.create({
        license_id: licenseId,
      });
    }

    // Update connection details
    connection.connection_type = dto.connection_type;
    connection.server_ip = dto.server_ip || null;
    connection.server_port = dto.server_port || null;
    connection.game_port = dto.game_port || null;
    connection.panel_api_endpoint = dto.panel_api_endpoint || null;
    connection.panel_server_identifier = dto.panel_server_identifier || null;

    // For bare metal, generate companion agent token
    if (dto.connection_type === 'bare_metal') {
      connection.companion_agent_token = crypto.randomBytes(32).toString('hex');
    }

    // Store encrypted API key if provided
    if (dto.panel_api_key) {
      const encryptionKey = this.configService.get<string>('encryption.key', '');
      const keyBuffer = Buffer.from(encryptionKey, 'hex');
      // AES-256-GCM needs a 32-byte key. An unset/short ENCRYPTION_KEY would
      // otherwise crash createCipheriv with an opaque "Invalid key length" 500.
      if (keyBuffer.length !== 32) {
        throw new ServiceUnavailableException(
          'Server encryption is not configured (ENCRYPTION_KEY must be 32 bytes / 64 hex chars). Contact the platform operator.',
        );
      }
      const iv = crypto.randomBytes(16);
      const cipher = crypto.createCipheriv('aes-256-gcm', keyBuffer, iv);
      const encrypted = Buffer.concat([
        cipher.update(dto.panel_api_key, 'utf8'),
        cipher.final(),
      ]);
      const authTag = cipher.getAuthTag();
      connection.panel_api_key_encrypted =
        `${iv.toString('hex')}:${encrypted.toString('hex')}:${authTag.toString('hex')}`;
    }

    connection.updated_at = new Date();

    const savedConnection = await this.connectionRepo.save(connection);

    // Create default configurations if they don't exist
    await this.createDefaultConfigs(licenseId);

    return savedConnection;
  }

  async completeSetup(licenseId: string): Promise<{ message: string }> {
    const connection = await this.connectionRepo.findOne({
      where: { license_id: licenseId },
    });

    if (connection) {
      // Bare-metal stays 'offline' until the agent's first heartbeat flips it
      // 'connected' (HostAgentConsumerService). Marking it connected here was a
      // false positive — the dashboard showed a live server before any agent
      // had checked in.
      if (connection.connection_type === 'bare_metal' && connection.connection_status !== 'connected') {
        connection.connection_status = 'offline';
        connection.updated_at = new Date();
        await this.connectionRepo.save(connection);
      }
    }

    return { message: 'Setup complete' };
  }

  private async createDefaultConfigs(licenseId: string): Promise<void> {
    // Create server config if not exists
    const existingConfig = await this.configRepo.findOne({
      where: { license_id: licenseId },
    });

    if (!existingConfig) {
      const config = this.configRepo.create({
        license_id: licenseId,
        server_name: 'My Rust Server',
      });
      await this.configRepo.save(config);
    }

    // Create notifications config if not exists
    const existingNotif = await this.notifConfigRepo.findOne({
      where: { license_id: licenseId },
    });

    if (!existingNotif) {
      const notifConfig = this.notifConfigRepo.create({
        license_id: licenseId,
      });
      await this.notifConfigRepo.save(notifConfig);
    }

    // Create public site config if not exists
    const existingPublic = await this.publicSiteRepo.findOne({
      where: { license_id: licenseId },
    });

    if (!existingPublic) {
      const publicConfig = this.publicSiteRepo.create({
        license_id: licenseId,
      });
      await this.publicSiteRepo.save(publicConfig);
    }

    // Create alert config if not exists
    const existingAlert = await this.alertConfigRepo.findOne({
      where: { license_id: licenseId },
    });

    if (!existingAlert) {
      const alertConfig = this.alertConfigRepo.create({
        license_id: licenseId,
      });
      await this.alertConfigRepo.save(alertConfig);
    }
  }
}
