/**
 * Payment Method Service
 * Manages payment methods for billing
 *
 * @module BillingUsage
 */

import { Repository, FindOptionsWhere } from 'typeorm';
import { AppDataSource } from '../../../shared/database/typeorm.config';
import {
  BillingPaymentMethod,
  PaymentProvider,
  PaymentMethodType,
} from '../entities/payment-method.entity';

export interface ServiceContext {
  tenantId: string;
  userId?: string;
}

export interface CreatePaymentMethodDto {
  provider: PaymentProvider;
  methodType: PaymentMethodType;
  providerCustomerId?: string;
  providerMethodId?: string;
  displayName?: string;
  cardBrand?: string;
  cardLastFour?: string;
  cardExpMonth?: number;
  cardExpYear?: number;
  bankName?: string;
  bankLastFour?: string;
  isDefault?: boolean;
  isVerified?: boolean;
}

export interface UpdatePaymentMethodDto {
  displayName?: string;
  isDefault?: boolean;
  isActive?: boolean;
  isVerified?: boolean;
}

export interface PaymentMethodFilters {
  provider?: PaymentProvider;
  methodType?: PaymentMethodType;
  isActive?: boolean;
  isDefault?: boolean;
}

export class PaymentMethodService {
  private repository: Repository<BillingPaymentMethod>;

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

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

    if (filters.provider) {
      where.provider = filters.provider;
    }

    if (filters.methodType) {
      where.methodType = filters.methodType;
    }

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

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

    return this.repository.find({
      where,
      order: { isDefault: 'DESC', createdAt: 'DESC' },
    });
  }

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

  async findDefault(ctx: ServiceContext): Promise<BillingPaymentMethod | null> {
    return this.repository.findOne({
      where: {
        tenantId: ctx.tenantId,
        isDefault: true,
        isActive: true,
      },
    });
  }

  async findByProviderId(
    ctx: ServiceContext,
    providerMethodId: string
  ): Promise<BillingPaymentMethod | null> {
    return this.repository.findOne({
      where: {
        tenantId: ctx.tenantId,
        providerMethodId,
      },
    });
  }

  async create(
    ctx: ServiceContext,
    data: CreatePaymentMethodDto
  ): Promise<BillingPaymentMethod> {
    // If this is the first payment method or marked as default, ensure it's the only default
    if (data.isDefault) {
      await this.clearDefaultFlag(ctx);
    }

    // If this is the first payment method, make it default
    const existingMethods = await this.repository.count({
      where: { tenantId: ctx.tenantId, isActive: true },
    });

    const method = this.repository.create({
      ...data,
      tenantId: ctx.tenantId,
      isDefault: data.isDefault || existingMethods === 0,
      isActive: true,
    });

    return this.repository.save(method);
  }

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

    // If setting as default, clear other defaults first
    if (data.isDefault) {
      await this.clearDefaultFlag(ctx);
    }

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

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

    await this.clearDefaultFlag(ctx);

    method.isDefault = true;
    return this.repository.save(method);
  }

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

    method.isActive = false;

    // If this was the default, try to set another as default
    if (method.isDefault) {
      method.isDefault = false;
      await this.repository.save(method);

      const otherMethod = await this.repository.findOne({
        where: {
          tenantId: ctx.tenantId,
          isActive: true,
        },
        order: { createdAt: 'ASC' },
      });

      if (otherMethod) {
        otherMethod.isDefault = true;
        await this.repository.save(otherMethod);
      }

      return method;
    }

    return this.repository.save(method);
  }

  async delete(ctx: ServiceContext, id: string): Promise<boolean> {
    const method = await this.findById(ctx, id);
    if (!method) {
      return false;
    }

    // Soft delete
    const result = await this.repository.softDelete(id);

    // If this was the default, set another as default
    if (method.isDefault) {
      const otherMethod = await this.repository.findOne({
        where: {
          tenantId: ctx.tenantId,
          isActive: true,
        },
        order: { createdAt: 'ASC' },
      });

      if (otherMethod) {
        otherMethod.isDefault = true;
        await this.repository.save(otherMethod);
      }
    }

    return result.affected ? result.affected > 0 : false;
  }

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

    method.isVerified = true;
    return this.repository.save(method);
  }

  async updateFromProvider(
    ctx: ServiceContext,
    providerMethodId: string,
    data: {
      cardBrand?: string;
      cardLastFour?: string;
      cardExpMonth?: number;
      cardExpYear?: number;
    }
  ): Promise<BillingPaymentMethod | null> {
    const method = await this.findByProviderId(ctx, providerMethodId);
    if (!method) {
      return null;
    }

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

  async getExpiringCards(
    ctx: ServiceContext,
    monthsAhead: number = 2
  ): Promise<BillingPaymentMethod[]> {
    const now = new Date();
    const targetMonth = now.getMonth() + monthsAhead;
    const targetYear =
      now.getFullYear() + Math.floor((now.getMonth() + monthsAhead) / 12);
    const normalizedMonth = targetMonth % 12 || 12;

    const methods = await this.repository.find({
      where: {
        tenantId: ctx.tenantId,
        methodType: 'card',
        isActive: true,
      },
    });

    return methods.filter((m) => {
      if (!m.cardExpYear || !m.cardExpMonth) return false;
      if (m.cardExpYear < targetYear) return true;
      if (m.cardExpYear === targetYear && m.cardExpMonth <= normalizedMonth)
        return true;
      return false;
    });
  }

  async getPaymentMethodStats(ctx: ServiceContext): Promise<{
    total: number;
    byProvider: Record<PaymentProvider, number>;
    byType: Record<PaymentMethodType, number>;
    hasDefault: boolean;
    expiringCount: number;
  }> {
    const methods = await this.repository.find({
      where: { tenantId: ctx.tenantId, isActive: true },
    });

    const byProvider: Record<PaymentProvider, number> = {
      stripe: 0,
      mercadopago: 0,
      bank_transfer: 0,
    };

    const byType: Record<PaymentMethodType, number> = {
      card: 0,
      bank_account: 0,
      wallet: 0,
    };

    let hasDefault = false;

    for (const method of methods) {
      byProvider[method.provider]++;
      byType[method.methodType]++;
      if (method.isDefault) hasDefault = true;
    }

    const expiringCards = await this.getExpiringCards(ctx, 2);

    return {
      total: methods.length,
      byProvider,
      byType,
      hasDefault,
      expiringCount: expiringCards.length,
    };
  }

  private async clearDefaultFlag(ctx: ServiceContext): Promise<void> {
    await this.repository.update(
      { tenantId: ctx.tenantId, isDefault: true },
      { isDefault: false }
    );
  }
}
