import { ContactList, ContactStatus } from "@prisma/client";
import {
  ContactListsRepository,
  ContactListWithCount,
  FindAllPaginatedByUserIdParams,
  FindAllPaginatedByUserIdResult,
} from "./contact-lists-repository";

interface Membership {
  contactId: string;
  listId: string;
}

interface ContactStatusRecord {
  id: string;
  status: ContactStatus;
}

export class InMemoryContactListsRepository implements ContactListsRepository {
  public items: ContactList[] = [];
  public memberships: Membership[] = [];
  public contacts: ContactStatusRecord[] = [];

  async findAllByUserId(userId: string): Promise<ContactListWithCount[]> {
    return this.items
      .filter((item) => item.userId === userId)
      .sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime())
      .map((item) => ({
        ...item,
        _count: {
          memberships: this.memberships.filter((m) => m.listId === item.id).length,
        },
      }));
  }

  async findAllPaginatedByUserId({
    userId,
    page,
    pageSize,
    search,
    marketingChannel,
    createdFrom,
    createdTo,
  }: FindAllPaginatedByUserIdParams): Promise<FindAllPaginatedByUserIdResult> {
    const filtered = this.items
      .filter((item) => item.userId === userId)
      .filter((item) => !search || item.name.toLowerCase().includes(search.toLowerCase()))
      .filter((item) => !marketingChannel || item.marketingChannel === marketingChannel)
      .filter((item) => !createdFrom || item.createdAt.getTime() >= createdFrom.getTime())
      .filter((item) => !createdTo || item.createdAt.getTime() <= createdTo.getTime())
      .sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());

    const total = filtered.length;
    const start = (page - 1) * pageSize;
    const items = filtered.slice(start, start + pageSize).map((item) => {
      const activeContactsCount = this.memberships.filter(
        (m) => m.listId === item.id && this.contacts.find((c) => c.id === m.contactId)?.status === "ACTIVE"
      ).length;
      return { ...item, activeContactsCount };
    });

    return { items, total };
  }

  async findById(id: string): Promise<ContactList | null> {
    return this.items.find((item) => item.id === id) ?? null;
  }

  async findByUserIdAndName(userId: string, name: string): Promise<ContactList | null> {
    return this.items.find((item) => item.userId === userId && item.name === name) ?? null;
  }

  async create(data: {
    name: string;
    description?: string;
    userId: string;
    marketingChannel?: ContactList["marketingChannel"];
  }): Promise<ContactList> {
    const contactList: ContactList = {
      id: `clist${this.items.length + 1}`,
      name: data.name,
      description: data.description ?? null,
      marketingChannel: data.marketingChannel ?? "EMAIL",
      userId: data.userId,
      createdAt: new Date(),
      updatedAt: new Date(),
    };

    this.items.push(contactList);
    return contactList;
  }

  async delete(id: string): Promise<void> {
    this.items = this.items.filter((item) => item.id !== id);
    this.memberships = this.memberships.filter((m) => m.listId !== id);
  }

  async addContactsToLists(contactIds: string[], listIds: string[]): Promise<void> {
    for (const contactId of contactIds) {
      for (const listId of listIds) {
        const exists = this.memberships.some((m) => m.contactId === contactId && m.listId === listId);
        if (!exists) {
          this.memberships.push({ contactId, listId });
        }
      }
    }
  }

  async removeContactFromList(contactId: string, listId: string): Promise<void> {
    this.memberships = this.memberships.filter(
      (m) => !(m.contactId === contactId && m.listId === listId)
    );
  }
}
