/**
 * Terminals Service
 *
 * Service for managing payment terminals
 */

import { Repository, DataSource } from 'typeorm';
import { BranchPaymentTerminal, HealthStatus } from '../../branches/entities/branch-payment-terminal.entity';
import { CreateTerminalDto, UpdateTerminalDto, TerminalResponseDto } from '../dto';

export class TerminalsService {
  private terminalRepository: Repository<BranchPaymentTerminal>;

  constructor(dataSource: DataSource) {
    this.terminalRepository = dataSource.getRepository(BranchPaymentTerminal);
  }

  /**
   * Create a new terminal
   */
  async create(_tenantId: string, dto: CreateTerminalDto): Promise<BranchPaymentTerminal> {
    // If setting as primary, unset other primary terminals for this branch
    if (dto.isPrimary) {
      await this.terminalRepository.update(
        { branchId: dto.branchId, isPrimary: true },
        { isPrimary: false }
      );
    }

    const terminal = this.terminalRepository.create({
      branchId: dto.branchId,
      terminalProvider: dto.terminalProvider,
      terminalId: dto.terminalId,
      terminalName: dto.terminalName,
      credentials: dto.credentials || {},
      isPrimary: dto.isPrimary || false,
      dailyLimit: dto.dailyLimit,
      transactionLimit: dto.transactionLimit,
      isActive: true,
      healthStatus: 'unknown',
    });

    return this.terminalRepository.save(terminal);
  }

  /**
   * Find terminals by branch
   */
  async findByBranch(branchId: string): Promise<TerminalResponseDto[]> {
    const terminals = await this.terminalRepository.find({
      where: { branchId, isActive: true },
      order: { isPrimary: 'DESC', createdAt: 'ASC' },
    });

    return terminals.map(this.toResponseDto);
  }

  /**
   * Find primary terminal for branch
   */
  async findPrimaryTerminal(branchId: string): Promise<BranchPaymentTerminal | null> {
    return this.terminalRepository.findOne({
      where: { branchId, isPrimary: true, isActive: true },
    });
  }

  /**
   * Find terminal by ID
   */
  async findById(id: string): Promise<BranchPaymentTerminal | null> {
    return this.terminalRepository.findOne({ where: { id } });
  }

  /**
   * Update terminal
   */
  async update(id: string, dto: UpdateTerminalDto): Promise<BranchPaymentTerminal> {
    const terminal = await this.findById(id);
    if (!terminal) {
      throw new Error('Terminal not found');
    }

    // If setting as primary, unset other primary terminals for this branch
    if (dto.isPrimary && !terminal.isPrimary) {
      await this.terminalRepository.update(
        { branchId: terminal.branchId, isPrimary: true },
        { isPrimary: false }
      );
    }

    Object.assign(terminal, dto);
    return this.terminalRepository.save(terminal);
  }

  /**
   * Delete terminal (soft delete by deactivating)
   */
  async delete(id: string): Promise<void> {
    const terminal = await this.findById(id);
    if (!terminal) {
      throw new Error('Terminal not found');
    }

    terminal.isActive = false;
    await this.terminalRepository.save(terminal);
  }

  /**
   * Set terminal as primary
   */
  async setPrimary(id: string): Promise<BranchPaymentTerminal> {
    const terminal = await this.findById(id);
    if (!terminal) {
      throw new Error('Terminal not found');
    }

    // Unset other primary terminals
    await this.terminalRepository.update(
      { branchId: terminal.branchId, isPrimary: true },
      { isPrimary: false }
    );

    terminal.isPrimary = true;
    return this.terminalRepository.save(terminal);
  }

  /**
   * Check terminal health
   */
  async checkHealth(id: string): Promise<{ status: HealthStatus; message: string }> {
    const terminal = await this.findById(id);
    if (!terminal) {
      throw new Error('Terminal not found');
    }

    // Simulate health check based on provider
    // In production, this would make an actual API call to the provider
    let status: HealthStatus = 'healthy';
    let message = 'Terminal is operational';

    try {
      // Simulate provider health check
      switch (terminal.terminalProvider) {
        case 'clip':
          // Check Clip terminal status
          break;
        case 'mercadopago':
          // Check MercadoPago terminal status
          break;
        case 'stripe':
          // Check Stripe terminal status
          break;
      }
    } catch (error: any) {
      status = 'offline';
      message = error.message || 'Failed to connect to terminal';
    }

    // Update terminal health status
    terminal.healthStatus = status;
    terminal.lastHealthCheckAt = new Date();
    await this.terminalRepository.save(terminal);

    return { status, message };
  }

  /**
   * Update health status (called after transactions)
   */
  async updateHealthStatus(id: string, status: HealthStatus): Promise<void> {
    await this.terminalRepository.update(id, {
      healthStatus: status,
      lastHealthCheckAt: new Date(),
    });
  }

  /**
   * Update last transaction timestamp
   */
  async updateLastTransaction(id: string): Promise<void> {
    await this.terminalRepository.update(id, {
      lastTransactionAt: new Date(),
      healthStatus: 'healthy', // If transaction works, terminal is healthy
      lastHealthCheckAt: new Date(),
    });
  }

  /**
   * Find terminals needing health check
   */
  async findTerminalsNeedingHealthCheck(maxAgeMinutes: number = 30): Promise<BranchPaymentTerminal[]> {
    const threshold = new Date();
    threshold.setMinutes(threshold.getMinutes() - maxAgeMinutes);

    return this.terminalRepository
      .createQueryBuilder('terminal')
      .where('terminal.isActive = true')
      .andWhere(
        '(terminal.lastHealthCheckAt IS NULL OR terminal.lastHealthCheckAt < :threshold)',
        { threshold }
      )
      .getMany();
  }

  /**
   * Convert entity to response DTO (without credentials)
   */
  private toResponseDto(terminal: BranchPaymentTerminal): TerminalResponseDto {
    return {
      id: terminal.id,
      branchId: terminal.branchId,
      terminalProvider: terminal.terminalProvider as TerminalResponseDto['terminalProvider'],
      terminalId: terminal.terminalId,
      terminalName: terminal.terminalName,
      isPrimary: terminal.isPrimary,
      isActive: terminal.isActive,
      dailyLimit: terminal.dailyLimit ? Number(terminal.dailyLimit) : undefined,
      transactionLimit: terminal.transactionLimit ? Number(terminal.transactionLimit) : undefined,
      healthStatus: terminal.healthStatus as TerminalResponseDto['healthStatus'],
      lastTransactionAt: terminal.lastTransactionAt,
      lastHealthCheckAt: terminal.lastHealthCheckAt,
    };
  }
}
