import { Repository, FindOptionsWhere, Like, ILike } from 'typeorm';
import { Person } from '../entities';
import { CreatePersonDto, UpdatePersonDto } from '../dto';

export interface PersonSearchParams {
  search?: string;
  email?: string;
  isVerified?: boolean;
  isResponsibleForTenant?: boolean;
  limit?: number;
  offset?: number;
}

export class PersonsService {
  constructor(private readonly personRepository: Repository<Person>) {}

  async findAll(params: PersonSearchParams = {}): Promise<{ data: Person[]; total: number }> {
    const { search, email, isVerified, isResponsibleForTenant, limit = 50, offset = 0 } = params;

    const where: FindOptionsWhere<Person>[] = [];
    const baseWhere: FindOptionsWhere<Person> = {};

    if (email) {
      baseWhere.email = email;
    }

    if (isVerified !== undefined) {
      baseWhere.isVerified = isVerified;
    }

    if (isResponsibleForTenant !== undefined) {
      baseWhere.isResponsibleForTenant = isResponsibleForTenant;
    }

    if (search) {
      where.push(
        { ...baseWhere, fullName: ILike(`%${search}%`) },
        { ...baseWhere, email: ILike(`%${search}%`) },
        { ...baseWhere, identificationNumber: ILike(`%${search}%`) }
      );
    } else {
      where.push(baseWhere);
    }

    const [data, total] = await this.personRepository.findAndCount({
      where,
      take: limit,
      skip: offset,
      order: { fullName: 'ASC' },
    });

    return { data, total };
  }

  async findOne(id: string): Promise<Person | null> {
    return this.personRepository.findOne({ where: { id } });
  }

  async findByEmail(email: string): Promise<Person | null> {
    return this.personRepository.findOne({ where: { email } });
  }

  async findByIdentification(type: string, number: string): Promise<Person | null> {
    return this.personRepository.findOne({
      where: { identificationType: type, identificationNumber: number },
    });
  }

  async create(dto: CreatePersonDto): Promise<Person> {
    // Check for existing email
    const existing = await this.findByEmail(dto.email);
    if (existing) {
      throw new Error('A person with this email already exists');
    }

    // Check for existing identification
    if (dto.identificationType && dto.identificationNumber) {
      const existingId = await this.findByIdentification(dto.identificationType, dto.identificationNumber);
      if (existingId) {
        throw new Error('A person with this identification already exists');
      }
    }

    const person = this.personRepository.create({
      ...dto,
      identificationExpiry: dto.identificationExpiry ? new Date(dto.identificationExpiry) : undefined,
    });

    return this.personRepository.save(person);
  }

  async update(id: string, dto: UpdatePersonDto): Promise<Person | null> {
    const person = await this.findOne(id);
    if (!person) return null;

    // If changing email, check for duplicates
    if (dto.email && dto.email !== person.email) {
      const existing = await this.findByEmail(dto.email);
      if (existing) {
        throw new Error('A person with this email already exists');
      }
    }

    // If changing identification, check for duplicates
    if (dto.identificationType && dto.identificationNumber) {
      const existingId = await this.findByIdentification(dto.identificationType, dto.identificationNumber);
      if (existingId && existingId.id !== id) {
        throw new Error('A person with this identification already exists');
      }
    }

    Object.assign(person, {
      ...dto,
      identificationExpiry: dto.identificationExpiry ? new Date(dto.identificationExpiry) : person.identificationExpiry,
    });

    return this.personRepository.save(person);
  }

  async delete(id: string): Promise<boolean> {
    const result = await this.personRepository.softDelete(id);
    return (result.affected ?? 0) > 0;
  }

  async verify(id: string, verifiedBy: string): Promise<Person | null> {
    const person = await this.findOne(id);
    if (!person) return null;

    person.isVerified = true;
    person.verifiedAt = new Date();
    person.verifiedBy = verifiedBy;

    return this.personRepository.save(person);
  }

  async unverify(id: string): Promise<Person | null> {
    const person = await this.findOne(id);
    if (!person) return null;

    person.isVerified = false;
    person.verifiedAt = null as any;
    person.verifiedBy = null as any;

    return this.personRepository.save(person);
  }

  async setAsResponsible(id: string, isResponsible: boolean = true): Promise<Person | null> {
    const person = await this.findOne(id);
    if (!person) return null;

    person.isResponsibleForTenant = isResponsible;

    return this.personRepository.save(person);
  }

  async getResponsiblePersons(): Promise<Person[]> {
    return this.personRepository.find({
      where: { isResponsibleForTenant: true },
      order: { fullName: 'ASC' },
    });
  }
}
