/**
 * Partner Contact Service
 * Servicio para gestion de contactos de socios comerciales
 *
 * @module Partners
 */

import { DataSource, Repository } from 'typeorm';
import { PartnerContact } from '../entities';

/**
 * Service context for multi-tenant operations
 */
export interface ServiceContext {
  tenantId: string;
  userId?: string;
}

export interface CreatePartnerContactDto {
  partnerId: string;
  fullName: string;
  position?: string;
  department?: string;
  email?: string;
  phone?: string;
  mobile?: string;
  extension?: string;
  isPrimary?: boolean;
  isBillingContact?: boolean;
  isShippingContact?: boolean;
  receivesNotifications?: boolean;
  notes?: string;
}

export interface UpdatePartnerContactDto {
  fullName?: string;
  position?: string;
  department?: string;
  email?: string;
  phone?: string;
  mobile?: string;
  extension?: string;
  isPrimary?: boolean;
  isBillingContact?: boolean;
  isShippingContact?: boolean;
  receivesNotifications?: boolean;
  notes?: string;
}

export interface PartnerContactFilters {
  isPrimary?: boolean;
  isBillingContact?: boolean;
  isShippingContact?: boolean;
  receivesNotifications?: boolean;
  search?: string;
}

export class PartnerContactService {
  private readonly repository: Repository<PartnerContact>;

  constructor(dataSource: DataSource) {
    this.repository = dataSource.getRepository(PartnerContact);
  }

  async findByPartnerId(
    partnerId: string,
    filters: PartnerContactFilters = {}
  ): Promise<PartnerContact[]> {
    const queryBuilder = this.repository
      .createQueryBuilder('contact')
      .where('contact.partner_id = :partnerId', { partnerId });

    if (filters.isPrimary !== undefined) {
      queryBuilder.andWhere('contact.is_primary = :isPrimary', {
        isPrimary: filters.isPrimary,
      });
    }

    if (filters.isBillingContact !== undefined) {
      queryBuilder.andWhere('contact.is_billing_contact = :isBillingContact', {
        isBillingContact: filters.isBillingContact,
      });
    }

    if (filters.isShippingContact !== undefined) {
      queryBuilder.andWhere('contact.is_shipping_contact = :isShippingContact', {
        isShippingContact: filters.isShippingContact,
      });
    }

    if (filters.receivesNotifications !== undefined) {
      queryBuilder.andWhere('contact.receives_notifications = :receivesNotifications', {
        receivesNotifications: filters.receivesNotifications,
      });
    }

    if (filters.search) {
      queryBuilder.andWhere(
        '(contact.full_name ILIKE :search OR contact.email ILIKE :search OR contact.position ILIKE :search)',
        { search: `%${filters.search}%` }
      );
    }

    return queryBuilder
      .orderBy('contact.is_primary', 'DESC')
      .addOrderBy('contact.full_name', 'ASC')
      .getMany();
  }

  async findById(id: string): Promise<PartnerContact | null> {
    return this.repository.findOne({
      where: { id },
      relations: ['partner'],
    });
  }

  async findPrimaryContact(partnerId: string): Promise<PartnerContact | null> {
    return this.repository.findOne({
      where: { partnerId, isPrimary: true },
    });
  }

  async findBillingContacts(partnerId: string): Promise<PartnerContact[]> {
    return this.repository.find({
      where: { partnerId, isBillingContact: true },
      order: { fullName: 'ASC' },
    });
  }

  async findShippingContacts(partnerId: string): Promise<PartnerContact[]> {
    return this.repository.find({
      where: { partnerId, isShippingContact: true },
      order: { fullName: 'ASC' },
    });
  }

  async findNotificationRecipients(partnerId: string): Promise<PartnerContact[]> {
    return this.repository.find({
      where: { partnerId, receivesNotifications: true },
      order: { fullName: 'ASC' },
    });
  }

  async create(
    _ctx: ServiceContext,
    dto: CreatePartnerContactDto
  ): Promise<PartnerContact> {
    // If this is primary, clear other primary contacts
    if (dto.isPrimary) {
      await this.clearPrimaryContact(dto.partnerId);
    }

    // Check if this is the first contact
    const existingContacts = await this.findByPartnerId(dto.partnerId);
    const isFirst = existingContacts.length === 0;

    const contact = this.repository.create({
      ...dto,
      isPrimary: dto.isPrimary ?? isFirst,
      receivesNotifications: dto.receivesNotifications ?? true,
    });

    return this.repository.save(contact);
  }

  async update(
    _ctx: ServiceContext,
    id: string,
    dto: UpdatePartnerContactDto
  ): Promise<PartnerContact | null> {
    const contact = await this.findById(id);
    if (!contact) {
      return null;
    }

    // If setting as primary, clear other primary contacts
    if (dto.isPrimary && !contact.isPrimary) {
      await this.clearPrimaryContact(contact.partnerId);
    }

    Object.assign(contact, dto);
    return this.repository.save(contact);
  }

  async setAsPrimary(id: string): Promise<PartnerContact | null> {
    const contact = await this.findById(id);
    if (!contact) {
      return null;
    }

    // Clear other primary contacts
    await this.clearPrimaryContact(contact.partnerId);

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

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

    const wasPrimary = contact.isPrimary;
    const partnerId = contact.partnerId;

    const result = await this.repository.delete({ id });

    // If deleted contact was primary, set another as primary
    if (wasPrimary && result.affected && result.affected > 0) {
      const remaining = await this.repository.findOne({
        where: { partnerId },
        order: { createdAt: 'ASC' },
      });

      if (remaining) {
        remaining.isPrimary = true;
        await this.repository.save(remaining);
      }
    }

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

  private async clearPrimaryContact(partnerId: string): Promise<void> {
    await this.repository.update(
      { partnerId, isPrimary: true },
      { isPrimary: false }
    );
  }

  async getContactSummary(partnerId: string): Promise<{
    total: number;
    hasPrimary: boolean;
    billingContacts: number;
    shippingContacts: number;
    notificationRecipients: number;
  }> {
    const contacts = await this.findByPartnerId(partnerId);

    return {
      total: contacts.length,
      hasPrimary: contacts.some(c => c.isPrimary),
      billingContacts: contacts.filter(c => c.isBillingContact).length,
      shippingContacts: contacts.filter(c => c.isShippingContact).length,
      notificationRecipients: contacts.filter(c => c.receivesNotifications).length,
    };
  }
}
