import { Injectable, NotFoundException, InternalServerErrorException, ServiceUnavailableException, Logger } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { ServerConnection } from '../../entities/server-connection.entity';
import { ServerConfig } from '../../entities/server-config.entity';
import { NatsService } from '../../services/nats.service';
import { InstancesService } from '../instances/instances.service';
import { UpdateServerConfigDto } from './dto/update-config.dto';
import { DeployServerDto } from './dto/deploy-server.dto';

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

  constructor(
    @InjectRepository(ServerConnection)
    private readonly connectionRepo: Repository<ServerConnection>,
    @InjectRepository(ServerConfig)
    private readonly configRepo: Repository<ServerConfig>,
    private readonly natsService: NatsService,
    private readonly instancesService: InstancesService,
  ) {}

  /**
   * NATS credentials the customer puts in their host agent's config so it can
   * authenticate to the per-license-scoped broker. Returns null if the broker
   * isn't enforcing auth yet (NATS_TOKEN_SECRET unset).
   */
  async getAgentCredentials(licenseId: string) {
    return this.natsService.getAgentCredentials(licenseId);
  }

  /**
   * Get server connection and config for a license.
   * Returns null fields if no server has been set up yet.
   */
  async getServer(licenseId: string) {
    const connection = await this.connectionRepo.findOne({
      where: { license_id: licenseId },
    });

    const config = await this.configRepo.findOne({
      where: { license_id: licenseId },
    });

    return {
      connection: connection || null,
      config: config || null,
      setup_required: !connection || !config,
    };
  }

  /**
   * Update server configuration
   */
  async updateConfig(licenseId: string, dto: UpdateServerConfigDto) {
    const config = await this.configRepo.findOne({
      where: { license_id: licenseId },
    });

    if (!config) {
      throw new NotFoundException('Server config not found');
    }

    // Apply updates
    Object.assign(config, dto);
    config.updated_at = new Date();

    return await this.configRepo.save(config);
  }

  /**
   * Send a console command to the server via the host agent (RCON)
   */
  async sendCommand(licenseId: string, command: string) {
    try {
      await this.instancesService.rconForLicense(licenseId, command);
      this.logger.log(`Console command dispatched for license ${licenseId}: ${command}`);
    } catch (err) {
      this.logger.error(`Failed to dispatch console command for license ${licenseId}: ${(err as Error).message}`);
      throw new InternalServerErrorException('Failed to dispatch command to server');
    }
    return { success: true, message: 'Command dispatched' };
  }

  /**
   * Start the server via the host agent
   */
  async startServer(licenseId: string) {
    await this.instancesService.lifecycleForLicense(licenseId, 'start');
    return { message: 'Start command sent' };
  }

  /**
   * Stop the server via the host agent
   */
  async stopServer(licenseId: string) {
    await this.instancesService.lifecycleForLicense(licenseId, 'stop');
    return { message: 'Stop command sent' };
  }

  /**
   * Restart the server via the host agent
   */
  async restartServer(licenseId: string) {
    await this.instancesService.lifecycleForLicense(licenseId, 'restart');
    return { message: 'Restart command sent' };
  }

  /**
   * Deploy Rust server — not yet supported via host agent.
   * Install the server manually and point the host agent at it.
   */
  async deployServer(_licenseId: string, _dto: DeployServerDto) {
    throw new ServiceUnavailableException(
      'Server deployment from the panel is coming soon — install the server and point the host agent at it for now.',
    );
  }

  /**
   * Install Oxide/uMod — not yet supported via host agent.
   */
  async installOxide(_licenseId: string) {
    throw new ServiceUnavailableException(
      'Oxide install from the panel is coming soon — install Oxide/uMod on the server for now.',
    );
  }
}
