import { Contact, ContactStatus, Prisma } from "@prisma/client";
import { prisma } from "@/shared/infra/prisma/prisma-client";
import {
  ContactListFilters,
  ContactsRepository,
  ContactWithLists,
  CreateContactData,
  FindPaginatedOptions,
  UpdateContactData,
} from "./contacts-repository";

function daysAgo(days: number): Date {
  return new Date(Date.now() - days * 24 * 60 * 60 * 1000);
}

function buildFilterWhere(filters: ContactListFilters): Prisma.ContactWhereInput {
  const where: Prisma.ContactWhereInput = {};

  if (filters.search) {
    where.OR = [
      { name: { contains: filters.search, mode: "insensitive" } },
      { email: { contains: filters.search, mode: "insensitive" } },
      { phone: { contains: filters.search, mode: "insensitive" } },
    ];
  }
  if (filters.tag) {
    where.tags = { has: filters.tag };
  }
  if (filters.listId) {
    where.listMemberships = { some: { listId: filters.listId } };
  }
  if (filters.status) {
    where.status = filters.status;
  }
  if (filters.openedWithinDays) {
    where.emailEvents = { some: { type: "OPEN", createdAt: { gte: daysAgo(filters.openedWithinDays) } } };
  }
  if (filters.subscribedWithinDays) {
    where.createdAt = { gte: daysAgo(filters.subscribedWithinDays) };
  }

  return where;
}

export class PrismaContactsRepository implements ContactsRepository {
  async findAll(): Promise<Contact[]> {
    return prisma.contact.findMany({
      orderBy: { createdAt: "desc" },
    });
  }

  async findAllWithLists(): Promise<ContactWithLists[]> {
    return prisma.contact.findMany({
      orderBy: { createdAt: "desc" },
      include: {
        listMemberships: {
          include: { list: { select: { id: true, name: true, marketingChannel: true } } },
        },
      },
    });
  }

  async findById(id: string): Promise<Contact | null> {
    return prisma.contact.findUnique({ where: { id } });
  }

  async findByIdWithLists(id: string): Promise<ContactWithLists | null> {
    return prisma.contact.findUnique({
      where: { id },
      include: {
        listMemberships: {
          include: { list: { select: { id: true, name: true, marketingChannel: true } } },
        },
      },
    });
  }

  async findAllActive(): Promise<Contact[]> {
    return prisma.contact.findMany({
      where: { status: ContactStatus.ACTIVE },
    });
  }

  async findActiveByListId(listId: string): Promise<Contact[]> {
    return prisma.contact.findMany({
      where: {
        status: ContactStatus.ACTIVE,
        listMemberships: { some: { listId } },
      },
    });
  }

  async createMany(emails: string[]): Promise<{ count: number }> {
    const result = await prisma.contact.createMany({
      data: emails.map((email) => ({ email })),
      skipDuplicates: true,
    });
    return { count: result.count };
  }

  async findManyByEmails(emails: string[]): Promise<Contact[]> {
    return prisma.contact.findMany({
      where: { email: { in: emails } },
    });
  }

  async delete(id: string): Promise<void> {
    await prisma.contact.delete({ where: { id } });
  }

  async deleteContactsByListId(listId: string): Promise<number> {
    // Encontra todos os contatos que estão SOMENTE nessa lista (exclusivos dela)
    const exclusiveContacts = await prisma.contact.findMany({
      where: {
        listMemberships: {
          every: { listId },
          some: { listId },
        },
      },
      select: { id: true },
    });

    if (exclusiveContacts.length === 0) return 0;

    const ids = exclusiveContacts.map((c) => c.id);
    const result = await prisma.contact.deleteMany({ where: { id: { in: ids } } });
    return result.count;
  }

  async updateStatus(id: string, status: ContactStatus): Promise<Contact> {
    return prisma.contact.update({ where: { id }, data: { status } });
  }

  async deleteMany(ids: string[]): Promise<number> {
    const result = await prisma.contact.deleteMany({ where: { id: { in: ids } } });
    return result.count;
  }

  async findPaginated({ skip, take, sortBy, sortDir, ...filters }: FindPaginatedOptions): Promise<ContactWithLists[]> {
    return prisma.contact.findMany({
      where: buildFilterWhere(filters),
      include: {
        listMemberships: {
          include: { list: { select: { id: true, name: true } } },
        },
      },
      orderBy: { [sortBy ?? "createdAt"]: sortDir ?? "desc" },
      skip,
      take,
    });
  }

  async countFiltered(filters: ContactListFilters): Promise<number> {
    return prisma.contact.count({ where: buildFilterWhere(filters) });
  }

  async create(data: CreateContactData): Promise<Contact> {
    return prisma.contact.create({
      data: {
        email: data.email,
        name: data.name ?? null,
        lastName: data.lastName ?? null,
        phone: data.phone ?? null,
        bairro: data.bairro ?? null,
        cidade: data.cidade ?? null,
        uf: data.uf ?? null,
        idioma: data.idioma ?? null,
        empresa: data.empresa ?? null,
        cep: data.cep ?? null,
        codigoEstabelecimento: data.codigoEstabelecimento ?? null,
        nomeEstabelecimento: data.nomeEstabelecimento ?? null,
        cdate: data.cdate ?? null,
        tags: data.tags ?? [],
        source: data.source ?? "MANUAL",
      },
    });
  }

  async update(id: string, data: UpdateContactData): Promise<Contact> {
    return prisma.contact.update({
      where: { id },
      data: {
        ...(data.status !== undefined && { status: data.status }),
        ...(data.name !== undefined && { name: data.name }),
        ...(data.lastName !== undefined && { lastName: data.lastName }),
        ...(data.phone !== undefined && { phone: data.phone }),
        ...(data.bairro !== undefined && { bairro: data.bairro }),
        ...(data.cidade !== undefined && { cidade: data.cidade }),
        ...(data.uf !== undefined && { uf: data.uf }),
        ...(data.idioma !== undefined && { idioma: data.idioma }),
        ...(data.empresa !== undefined && { empresa: data.empresa }),
        ...(data.cep !== undefined && { cep: data.cep }),
        ...(data.codigoEstabelecimento !== undefined && { codigoEstabelecimento: data.codigoEstabelecimento }),
        ...(data.nomeEstabelecimento !== undefined && { nomeEstabelecimento: data.nomeEstabelecimento }),
        ...(data.cdate !== undefined && { cdate: data.cdate }),
        ...(data.tags !== undefined && { tags: data.tags }),
      },
    });
  }

  async addTagsToMany(ids: string[], tags: string[], userId?: string): Promise<void> {
    const contacts = await prisma.contact.findMany({ where: { id: { in: ids } }, select: { id: true, tags: true } });

    await prisma.$transaction(
      contacts.map((contact) =>
        prisma.contact.update({
          where: { id: contact.id },
          data: { tags: [...new Set([...contact.tags, ...tags])] },
        })
      )
    );

    if (userId && tags.length > 0) {
      for (const tagName of tags) {
        const trimmed = tagName.trim();
        if (!trimmed) continue;
        const exists = await prisma.tag.findFirst({
          where: { userId, name: { equals: trimmed, mode: "insensitive" } },
        });
        if (!exists) {
          await prisma.tag.create({
            data: { userId, name: trimmed },
          }).catch(() => {});
        }
      }
    }
  }

  async upsertByEmail(data: CreateContactData): Promise<{ contact: Contact; created: boolean }> {
    const existing = await prisma.contact.findUnique({ where: { email: data.email } });

    if (!existing) {
      const contact = await this.create(data);
      return { contact, created: true };
    }

    const contact = await prisma.contact.update({
      where: { id: existing.id },
      data: {
        ...(data.name !== undefined && data.name !== null && { name: data.name }),
        ...(data.phone !== undefined && data.phone !== null && { phone: data.phone }),
        ...(data.tags?.length && { tags: [...new Set([...existing.tags, ...data.tags])] }),
      },
    });
    return { contact, created: false };
  }
}
