import { query, queryOne } from '../../config/database.js';
import { NotFoundError, ConflictError } from '../../shared/errors/index.js';

export interface CustomerGroupMember {
  id: string;
  customer_group_id: string;
  partner_id: string;
  partner_name?: string;
  joined_at: Date;
}

export interface CustomerGroup {
  id: string;
  tenant_id: string;
  name: string;
  description?: string;
  discount_percentage: number;
  members?: CustomerGroupMember[];
  member_count?: number;
  created_at: Date;
}

export interface CreateCustomerGroupDto {
  name: string;
  description?: string;
  discount_percentage?: number;
}

export interface UpdateCustomerGroupDto {
  name?: string;
  description?: string | null;
  discount_percentage?: number;
}

export interface CustomerGroupFilters {
  search?: string;
  page?: number;
  limit?: number;
}

class CustomerGroupsService {
  async findAll(tenantId: string, filters: CustomerGroupFilters = {}): Promise<{ data: CustomerGroup[]; total: number }> {
    const { search, page = 1, limit = 20 } = filters;
    const offset = (page - 1) * limit;

    let whereClause = 'WHERE cg.tenant_id = $1';
    const params: any[] = [tenantId];
    let paramIndex = 2;

    if (search) {
      whereClause += ` AND (cg.name ILIKE $${paramIndex} OR cg.description ILIKE $${paramIndex})`;
      params.push(`%${search}%`);
      paramIndex++;
    }

    const countResult = await queryOne<{ count: string }>(
      `SELECT COUNT(*) as count FROM sales.customer_groups cg ${whereClause}`,
      params
    );

    params.push(limit, offset);
    const data = await query<CustomerGroup>(
      `SELECT cg.*,
              (SELECT COUNT(*) FROM sales.customer_group_members cgm WHERE cgm.customer_group_id = cg.id) as member_count
       FROM sales.customer_groups cg
       ${whereClause}
       ORDER BY cg.name
       LIMIT $${paramIndex} OFFSET $${paramIndex + 1}`,
      params
    );

    return {
      data,
      total: parseInt(countResult?.count || '0', 10),
    };
  }

  async findById(id: string, tenantId: string): Promise<CustomerGroup> {
    const group = await queryOne<CustomerGroup>(
      `SELECT cg.*,
              (SELECT COUNT(*) FROM sales.customer_group_members cgm WHERE cgm.customer_group_id = cg.id) as member_count
       FROM sales.customer_groups cg
       WHERE cg.id = $1 AND cg.tenant_id = $2`,
      [id, tenantId]
    );

    if (!group) {
      throw new NotFoundError('Grupo de clientes no encontrado');
    }

    // Get members
    const members = await query<CustomerGroupMember>(
      `SELECT cgm.*,
              p.name as partner_name
       FROM sales.customer_group_members cgm
       LEFT JOIN core.partners p ON cgm.partner_id = p.id
       WHERE cgm.customer_group_id = $1
       ORDER BY p.name`,
      [id]
    );

    group.members = members;

    return group;
  }

  async create(dto: CreateCustomerGroupDto, tenantId: string, userId: string): Promise<CustomerGroup> {
    // Check unique name
    const existing = await queryOne(
      `SELECT id FROM sales.customer_groups WHERE tenant_id = $1 AND name = $2`,
      [tenantId, dto.name]
    );

    if (existing) {
      throw new ConflictError('Ya existe un grupo de clientes con ese nombre');
    }

    const group = await queryOne<CustomerGroup>(
      `INSERT INTO sales.customer_groups (tenant_id, name, description, discount_percentage, created_by)
       VALUES ($1, $2, $3, $4, $5)
       RETURNING *`,
      [tenantId, dto.name, dto.description, dto.discount_percentage || 0, userId]
    );

    return group!;
  }

  async update(id: string, dto: UpdateCustomerGroupDto, tenantId: string): Promise<CustomerGroup> {
    await this.findById(id, tenantId);

    const updateFields: string[] = [];
    const values: any[] = [];
    let paramIndex = 1;

    if (dto.name !== undefined) {
      // Check unique name
      const existing = await queryOne(
        `SELECT id FROM sales.customer_groups WHERE tenant_id = $1 AND name = $2 AND id != $3`,
        [tenantId, dto.name, id]
      );
      if (existing) {
        throw new ConflictError('Ya existe un grupo de clientes con ese nombre');
      }
      updateFields.push(`name = $${paramIndex++}`);
      values.push(dto.name);
    }
    if (dto.description !== undefined) {
      updateFields.push(`description = $${paramIndex++}`);
      values.push(dto.description);
    }
    if (dto.discount_percentage !== undefined) {
      updateFields.push(`discount_percentage = $${paramIndex++}`);
      values.push(dto.discount_percentage);
    }

    values.push(id, tenantId);

    await query(
      `UPDATE sales.customer_groups SET ${updateFields.join(', ')}
       WHERE id = $${paramIndex++} AND tenant_id = $${paramIndex}`,
      values
    );

    return this.findById(id, tenantId);
  }

  async delete(id: string, tenantId: string): Promise<void> {
    const group = await this.findById(id, tenantId);

    if (group.member_count && group.member_count > 0) {
      throw new ConflictError('No se puede eliminar un grupo con miembros');
    }

    await query(`DELETE FROM sales.customer_groups WHERE id = $1 AND tenant_id = $2`, [id, tenantId]);
  }

  async addMember(groupId: string, partnerId: string, tenantId: string): Promise<CustomerGroupMember> {
    await this.findById(groupId, tenantId);

    // Check if already member
    const existing = await queryOne(
      `SELECT id FROM sales.customer_group_members WHERE customer_group_id = $1 AND partner_id = $2`,
      [groupId, partnerId]
    );
    if (existing) {
      throw new ConflictError('El cliente ya es miembro de este grupo');
    }

    const member = await queryOne<CustomerGroupMember>(
      `INSERT INTO sales.customer_group_members (customer_group_id, partner_id)
       VALUES ($1, $2)
       RETURNING *`,
      [groupId, partnerId]
    );

    return member!;
  }

  async removeMember(groupId: string, memberId: string, tenantId: string): Promise<void> {
    await this.findById(groupId, tenantId);

    await query(
      `DELETE FROM sales.customer_group_members WHERE id = $1 AND customer_group_id = $2`,
      [memberId, groupId]
    );
  }
}

export const customerGroupsService = new CustomerGroupsService();
