/**
 * Partner Segment Service
 * Servicio para gestion de segmentos de socios comerciales
 *
 * @module Partners
 */

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

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

export type SegmentType = 'customer' | 'supplier' | 'both';

export interface CreatePartnerSegmentDto {
  code: string;
  name: string;
  description?: string;
  segmentType: SegmentType;
  color?: string;
  icon?: string;
  rules?: Record<string, unknown>;
  defaultDiscount?: number;
  defaultPaymentTerms?: number;
  priority?: number;
  sortOrder?: number;
}

export interface UpdatePartnerSegmentDto {
  name?: string;
  description?: string;
  segmentType?: SegmentType;
  color?: string;
  icon?: string;
  rules?: Record<string, unknown>;
  defaultDiscount?: number;
  defaultPaymentTerms?: number;
  priority?: number;
  isActive?: boolean;
  sortOrder?: number;
}

export interface PartnerSegmentFilters {
  segmentType?: SegmentType;
  isActive?: boolean;
  search?: string;
}

export class PartnerSegmentService {
  private readonly repository: Repository<PartnerSegment>;

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

  async findAll(
    ctx: ServiceContext,
    filters: PartnerSegmentFilters = {}
  ): Promise<PartnerSegment[]> {
    const queryBuilder = this.repository
      .createQueryBuilder('segment')
      .where('segment.tenant_id = :tenantId', { tenantId: ctx.tenantId });

    if (filters.segmentType) {
      queryBuilder.andWhere(
        '(segment.segment_type = :segmentType OR segment.segment_type = :both)',
        { segmentType: filters.segmentType, both: 'both' }
      );
    }

    if (filters.isActive !== undefined) {
      queryBuilder.andWhere('segment.is_active = :isActive', {
        isActive: filters.isActive,
      });
    }

    if (filters.search) {
      queryBuilder.andWhere(
        '(segment.name ILIKE :search OR segment.code ILIKE :search OR segment.description ILIKE :search)',
        { search: `%${filters.search}%` }
      );
    }

    return queryBuilder
      .orderBy('segment.sort_order', 'ASC')
      .addOrderBy('segment.name', 'ASC')
      .getMany();
  }

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

  async findByCode(ctx: ServiceContext, code: string): Promise<PartnerSegment | null> {
    return this.repository.findOne({
      where: {
        code,
        tenantId: ctx.tenantId,
      },
    });
  }

  async findActiveByType(
    ctx: ServiceContext,
    segmentType: SegmentType
  ): Promise<PartnerSegment[]> {
    return this.findAll(ctx, { segmentType, isActive: true });
  }

  async findCustomerSegments(ctx: ServiceContext): Promise<PartnerSegment[]> {
    return this.findActiveByType(ctx, 'customer');
  }

  async findSupplierSegments(ctx: ServiceContext): Promise<PartnerSegment[]> {
    return this.findActiveByType(ctx, 'supplier');
  }

  async create(
    ctx: ServiceContext,
    dto: CreatePartnerSegmentDto
  ): Promise<PartnerSegment> {
    // Check for existing code
    const existingCode = await this.findByCode(ctx, dto.code);
    if (existingCode) {
      throw new Error('A segment with this code already exists');
    }

    // Get next sort order if not provided
    let sortOrder = dto.sortOrder;
    if (sortOrder === undefined) {
      const lastSegment = await this.repository.findOne({
        where: { tenantId: ctx.tenantId },
        order: { sortOrder: 'DESC' },
      });
      sortOrder = lastSegment ? lastSegment.sortOrder + 1 : 0;
    }

    const segment = this.repository.create({
      tenantId: ctx.tenantId,
      createdBy: ctx.userId,
      ...dto,
      sortOrder,
      defaultDiscount: dto.defaultDiscount ?? 0,
      defaultPaymentTerms: dto.defaultPaymentTerms ?? 0,
      priority: dto.priority ?? 0,
    });

    return this.repository.save(segment);
  }

  async update(
    ctx: ServiceContext,
    id: string,
    dto: UpdatePartnerSegmentDto
  ): Promise<PartnerSegment | null> {
    const segment = await this.findById(ctx, id);
    if (!segment) {
      return null;
    }

    Object.assign(segment, {
      ...dto,
      updatedBy: ctx.userId,
    });

    return this.repository.save(segment);
  }

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

    segment.isActive = true;
    segment.updatedBy = ctx.userId ?? '';

    return this.repository.save(segment);
  }

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

    segment.isActive = false;
    segment.updatedBy = ctx.userId ?? '';

    return this.repository.save(segment);
  }

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

    const result = await this.repository.delete({
      id,
      tenantId: ctx.tenantId,
    });

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

  async reorder(
    ctx: ServiceContext,
    orderedIds: string[]
  ): Promise<PartnerSegment[]> {
    const segments = await this.findAll(ctx);

    // Create a map for quick lookup
    const segmentMap = new Map(segments.map(s => [s.id, s]));

    // Update sort orders
    const updates = orderedIds.map(async (id, index) => {
      const segment = segmentMap.get(id);
      if (segment) {
        segment.sortOrder = index;
        segment.updatedBy = ctx.userId ?? '';
        return this.repository.save(segment);
      }
      return null;
    });

    await Promise.all(updates);

    return this.findAll(ctx);
  }

  async getStatistics(ctx: ServiceContext): Promise<{
    total: number;
    active: number;
    inactive: number;
    byType: Record<SegmentType, number>;
  }> {
    const segments = await this.findAll(ctx);

    const byType: Record<SegmentType, number> = {
      customer: 0,
      supplier: 0,
      both: 0,
    };

    segments.forEach(s => {
      byType[s.segmentType]++;
    });

    return {
      total: segments.length,
      active: segments.filter(s => s.isActive).length,
      inactive: segments.filter(s => !s.isActive).length,
      byType,
    };
  }
}
