/**
 * BranchPaymentTerminal Service
 * Servicio para gestión de terminales de pago por sucursal
 *
 * @module Branches
 */

import { Repository, FindOptionsWhere } from 'typeorm';
import { AppDataSource } from '../../../shared/database/typeorm.config';
import {
  BranchPaymentTerminal,
  TerminalProvider,
  HealthStatus,
} from '../entities/branch-payment-terminal.entity';
import { ServiceContext } from './branch.service';

export interface CreateBranchPaymentTerminalDto {
  branchId: string;
  terminalId: string;
  terminalName?: string;
  terminalProvider: TerminalProvider;
  provider: string;
  credentials?: Record<string, any>;
  isActive?: boolean;
  isPrimary?: boolean;
  dailyLimit?: number;
  transactionLimit?: number;
  config?: Record<string, any>;
}

export interface UpdateBranchPaymentTerminalDto {
  terminalName?: string;
  terminalProvider?: TerminalProvider;
  provider?: string;
  credentials?: Record<string, any>;
  isActive?: boolean;
  isPrimary?: boolean;
  healthStatus?: HealthStatus;
  dailyLimit?: number;
  transactionLimit?: number;
  config?: Record<string, any>;
}

export interface BranchPaymentTerminalFilters {
  branchId?: string;
  terminalProvider?: TerminalProvider;
  isActive?: boolean;
  healthStatus?: HealthStatus;
}

export class BranchPaymentTerminalService {
  private repository: Repository<BranchPaymentTerminal>;

  constructor() {
    this.repository = AppDataSource.getRepository(BranchPaymentTerminal);
  }

  async findAll(
    ctx: ServiceContext,
    filters?: BranchPaymentTerminalFilters
  ): Promise<BranchPaymentTerminal[]> {
    const where: FindOptionsWhere<BranchPaymentTerminal> = {
      tenantId: ctx.tenantId,
    };

    if (filters?.branchId) {
      where.branchId = filters.branchId;
    }

    if (filters?.terminalProvider) {
      where.terminalProvider = filters.terminalProvider;
    }

    if (filters?.isActive !== undefined) {
      where.isActive = filters.isActive;
    }

    if (filters?.healthStatus) {
      where.healthStatus = filters.healthStatus;
    }

    return this.repository.find({
      where,
      order: { isPrimary: 'DESC', terminalName: 'ASC' },
    });
  }

  async findById(ctx: ServiceContext, id: string): Promise<BranchPaymentTerminal | null> {
    return this.repository.findOne({
      where: { id, tenantId: ctx.tenantId },
    });
  }

  async findByBranch(ctx: ServiceContext, branchId: string): Promise<BranchPaymentTerminal[]> {
    return this.repository.find({
      where: { branchId, tenantId: ctx.tenantId, isActive: true },
      order: { isPrimary: 'DESC', terminalName: 'ASC' },
    });
  }

  async findPrimaryTerminal(
    ctx: ServiceContext,
    branchId: string
  ): Promise<BranchPaymentTerminal | null> {
    return this.repository.findOne({
      where: { branchId, tenantId: ctx.tenantId, isPrimary: true, isActive: true },
    });
  }

  async findByTerminalId(
    ctx: ServiceContext,
    terminalId: string
  ): Promise<BranchPaymentTerminal | null> {
    return this.repository.findOne({
      where: { terminalId, tenantId: ctx.tenantId },
    });
  }

  async create(
    ctx: ServiceContext,
    data: CreateBranchPaymentTerminalDto
  ): Promise<BranchPaymentTerminal> {
    // If this is the first terminal or marked as primary, ensure only one primary
    if (data.isPrimary) {
      await this.repository.update(
        { branchId: data.branchId, tenantId: ctx.tenantId, isPrimary: true },
        { isPrimary: false }
      );
    }

    const terminal = this.repository.create({
      ...data,
      tenantId: ctx.tenantId,
      healthStatus: 'unknown',
    });

    return this.repository.save(terminal);
  }

  async update(
    ctx: ServiceContext,
    id: string,
    data: UpdateBranchPaymentTerminalDto
  ): Promise<BranchPaymentTerminal | null> {
    const terminal = await this.findById(ctx, id);
    if (!terminal) {
      return null;
    }

    // If setting as primary, unset other primaries for this branch
    if (data.isPrimary && !terminal.isPrimary) {
      await this.repository.update(
        { branchId: terminal.branchId, tenantId: ctx.tenantId, isPrimary: true },
        { isPrimary: false }
      );
    }

    Object.assign(terminal, data);
    return this.repository.save(terminal);
  }

  async delete(ctx: ServiceContext, id: string): Promise<boolean> {
    const result = await this.repository.delete({ id, tenantId: ctx.tenantId });
    return result.affected ? result.affected > 0 : false;
  }

  async setAsPrimary(ctx: ServiceContext, id: string): Promise<BranchPaymentTerminal | null> {
    const terminal = await this.findById(ctx, id);
    if (!terminal) {
      return null;
    }

    // Unset other primaries
    await this.repository.update(
      { branchId: terminal.branchId, tenantId: ctx.tenantId, isPrimary: true },
      { isPrimary: false }
    );

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

  async updateHealthStatus(
    ctx: ServiceContext,
    id: string,
    healthStatus: HealthStatus
  ): Promise<BranchPaymentTerminal | null> {
    const terminal = await this.findById(ctx, id);
    if (!terminal) {
      return null;
    }

    terminal.healthStatus = healthStatus;
    terminal.lastHealthCheckAt = new Date();
    return this.repository.save(terminal);
  }

  async recordTransaction(ctx: ServiceContext, id: string): Promise<BranchPaymentTerminal | null> {
    const terminal = await this.findById(ctx, id);
    if (!terminal) {
      return null;
    }

    terminal.lastTransactionAt = new Date();
    return this.repository.save(terminal);
  }

  async getHealthSummary(ctx: ServiceContext): Promise<{
    total: number;
    healthy: number;
    degraded: number;
    offline: number;
    unknown: number;
  }> {
    const terminals = await this.repository.find({
      where: { tenantId: ctx.tenantId, isActive: true },
    });

    return {
      total: terminals.length,
      healthy: terminals.filter(t => t.healthStatus === 'healthy').length,
      degraded: terminals.filter(t => t.healthStatus === 'degraded').length,
      offline: terminals.filter(t => t.healthStatus === 'offline').length,
      unknown: terminals.filter(t => t.healthStatus === 'unknown').length,
    };
  }

  async getTerminalsNeedingHealthCheck(
    ctx: ServiceContext,
    maxAgeMinutes: number = 30
  ): Promise<BranchPaymentTerminal[]> {
    const cutoff = new Date(Date.now() - maxAgeMinutes * 60 * 1000);

    const terminals = await this.repository.find({
      where: { tenantId: ctx.tenantId, isActive: true },
    });

    return terminals.filter(t => !t.lastHealthCheckAt || t.lastHealthCheckAt < cutoff);
  }
}
