import { describe, it, expect, beforeEach } from "vitest";
import { Contact, ContactStatus } from "@prisma/client";
import { InMemoryContactsRepository } from "../repositories/in-memory-contacts-repository";
import { GetContactsPaginatedUseCase } from "./get-contacts-paginated-use-case";

let contactsRepository: InMemoryContactsRepository;
let sut: GetContactsPaginatedUseCase;

function makeContact(overrides: Partial<Contact> = {}): Contact {
  return {
    id: `contact-${Math.random()}`,
    email: "contact@example.com",
    name: "Contact",
    lastName: null,
    phone: null,
    bairro: null,
    cidade: null,
    uf: null,
    idioma: null,
    empresa: null,
    cep: null,
    codigoEstabelecimento: null,
    nomeEstabelecimento: null,
    cdate: null,
    tags: [],
    source: "MANUAL",
    status: ContactStatus.ACTIVE,
    createdAt: new Date(),
    updatedAt: new Date(),
    ...overrides,
  };
}

describe("Get Contacts Paginated Use Case", () => {
  beforeEach(() => {
    contactsRepository = new InMemoryContactsRepository();
    sut = new GetContactsPaginatedUseCase(contactsRepository);
  });

  it("paginates results and reports total independent of the page size", async () => {
    for (let i = 1; i <= 25; i++) {
      contactsRepository.items.push(makeContact({ id: `contact-${i}`, email: `c${i}@example.com` }));
    }

    const page1 = await sut.execute({ page: 1, pageSize: 10 });
    expect(page1.items).toHaveLength(10);
    expect(page1.total).toBe(25);

    const page3 = await sut.execute({ page: 3, pageSize: 10 });
    expect(page3.items).toHaveLength(5);
  });

  it("filters by search across name, email and phone", async () => {
    contactsRepository.items.push(makeContact({ id: "1", name: "Maria Silva", email: "maria@example.com" }));
    contactsRepository.items.push(makeContact({ id: "2", name: "João Souza", email: "joao@example.com" }));

    const result = await sut.execute({ page: 1, pageSize: 20, search: "maria" });

    expect(result.total).toBe(1);
    expect(result.items[0].id).toBe("1");
  });

  it("filters by tag", async () => {
    contactsRepository.items.push(makeContact({ id: "1", tags: ["vip"] }));
    contactsRepository.items.push(makeContact({ id: "2", tags: ["lead"] }));

    const result = await sut.execute({ page: 1, pageSize: 20, tag: "vip" });

    expect(result.total).toBe(1);
    expect(result.items[0].id).toBe("1");
  });

  it("filters by status", async () => {
    contactsRepository.items.push(makeContact({ id: "1", status: ContactStatus.ACTIVE }));
    contactsRepository.items.push(makeContact({ id: "2", status: ContactStatus.BOUNCED }));

    const result = await sut.execute({ page: 1, pageSize: 20, status: ContactStatus.BOUNCED });

    expect(result.total).toBe(1);
    expect(result.items[0].id).toBe("2");
  });

  it("filters by listId", async () => {
    contactsRepository.items.push(makeContact({ id: "1" }));
    contactsRepository.items.push(makeContact({ id: "2" }));
    contactsRepository.listMemberships.push({ contactId: "1", listId: "list-1", listName: "Lista A" });

    const result = await sut.execute({ page: 1, pageSize: 20, listId: "list-1" });

    expect(result.total).toBe(1);
    expect(result.items[0].id).toBe("1");
  });

  it("combines multiple filters (search + status)", async () => {
    contactsRepository.items.push(
      makeContact({ id: "1", name: "Maria Silva", email: "maria@example.com", status: ContactStatus.ACTIVE })
    );
    contactsRepository.items.push(
      makeContact({ id: "2", name: "Maria Souza", email: "maria2@example.com", status: ContactStatus.BOUNCED })
    );

    const result = await sut.execute({ page: 1, pageSize: 20, search: "maria", status: ContactStatus.ACTIVE });

    expect(result.total).toBe(1);
    expect(result.items[0].id).toBe("1");
  });

  it("filters contacts opened within N days using the opened-contact marker", async () => {
    contactsRepository.items.push(makeContact({ id: "1" }));
    contactsRepository.items.push(makeContact({ id: "2" }));
    contactsRepository.openedContactIds.add("1");

    const result = await sut.execute({ page: 1, pageSize: 20, openedWithinDays: 30 });

    expect(result.total).toBe(1);
    expect(result.items[0].id).toBe("1");
  });

  it("filters contacts subscribed within N days", async () => {
    const now = new Date();
    const old = new Date(now.getTime() - 60 * 24 * 60 * 60 * 1000);
    contactsRepository.items.push(makeContact({ id: "1", createdAt: now }));
    contactsRepository.items.push(makeContact({ id: "2", createdAt: old }));

    const result = await sut.execute({ page: 1, pageSize: 20, subscribedWithinDays: 30 });

    expect(result.total).toBe(1);
    expect(result.items[0].id).toBe("1");
  });

  it("sorts by name ascending and descending", async () => {
    contactsRepository.items.push(makeContact({ id: "1", name: "Zeca" }));
    contactsRepository.items.push(makeContact({ id: "2", name: "Ana" }));

    const asc = await sut.execute({ page: 1, pageSize: 20, sortBy: "name", sortDir: "asc" });
    expect(asc.items.map((i) => i.id)).toEqual(["2", "1"]);

    const desc = await sut.execute({ page: 1, pageSize: 20, sortBy: "name", sortDir: "desc" });
    expect(desc.items.map((i) => i.id)).toEqual(["1", "2"]);
  });
});
