import type { InboxAdapter } from '../inbox/adapter';
import type { InboxItem, InboxState, MailAttachment, MailMessage, MailPerson } from '../inbox/types';

const PEOPLE: MailPerson[] = [
  { id: 'pp_sofia', name: 'Sofia Ramirez', kind: 'staff' },
  { id: 'pp_dan', name: 'Dan Whitaker', kind: 'staff' },
  { id: 'cust_acme', name: 'Acme Roofing (Client)', kind: 'customer' },
];

interface MockMail {
  subject: string;
  messages: MailMessage[];
}

/**
 * In-memory inbox for demos/tests. State is PER INSTANCE. Seeds a few work items (mention,
 * needs-reply, alert) plus mail threads that fold into the Open view as MAIL rows.
 */
export class MockInboxAdapter implements InboxAdapter {
  private seq = 100;
  private readonly items: InboxItem[];
  private readonly threads = new Map<string, MockMail>();
  /** threadIds that surface as standalone MAIL rows (in addition to work items). */
  private readonly mailFold = ['mt_welcome', 'mt_invoice'];

  constructor(private readonly now: () => string = () => new Date().toISOString()) {
    const at = this.now();
    this.items = [
      { id: 'in_1', kind: 'MENTION', state: 'OPEN', title: 'Sofia mentioned you', summary: '@you — can you confirm the Henderson scope?', priority: 'HIGH', threadId: 'mt_mention', createdAt: at },
      { id: 'in_2', kind: 'NEEDS_REPLY', state: 'OPEN', title: 'Reply needed — Storm response', summary: 'Dan: crew rolling out at 7', priority: 'MEDIUM', threadId: 'mt_storm', createdAt: at },
      { id: 'in_3', kind: 'SYSTEM_ALERT', state: 'OPEN', title: 'Ticket TK-204 updated', summary: 'Customer replied on the roof-leak case.', priority: 'LOW', createdAt: at },
    ];
    this.threads.set('mt_mention', {
      subject: 'Henderson scope',
      messages: [{ id: 'm1', actorId: 'pp_sofia', kind: 'EMAIL', at, html: '<p>Can you confirm the <b>Henderson</b> scope by EOD?</p>', text: 'Can you confirm the Henderson scope by EOD?', attachment: null }],
    });
    this.threads.set('mt_storm', {
      subject: 'Storm response — East side',
      messages: [{ id: 'm2', actorId: 'pp_dan', kind: 'EMAIL', at, html: '<p>Crew is rolling out at 7. Confirm the Henderson job?</p>', text: 'Crew rolling out at 7. Confirm the Henderson job?', attachment: null }],
    });
    this.threads.set('mt_welcome', {
      subject: 'Welcome to the Founders Club',
      messages: [{ id: 'm3', actorId: 'system', kind: 'EMAIL', at, html: '<p>Thanks for joining the <b>Founders Club</b>. Set up your account to get started.</p>', text: 'Thanks for joining the Founders Club.', attachment: null }],
    });
    this.threads.set('mt_invoice', {
      subject: 'Invoice #1042 — Acme Roofing',
      messages: [{ id: 'm4', actorId: 'cust_acme', kind: 'EMAIL', at, html: '<p>Attached is invoice <b>#1042</b> for the East-side job.</p>', text: 'Attached is invoice #1042 for the East-side job.', attachment: null }],
    });
  }

  async listInbox(state?: InboxState): Promise<InboxItem[]> {
    const showMail = !state || state === 'OPEN';
    const work = this.items.filter((i) => (state ? i.state === state : true));
    const mail: InboxItem[] = showMail
      ? this.mailFold.map((tid) => {
          const t = this.threads.get(tid)!;
          const last = t.messages[t.messages.length - 1];
          return {
            id: `mail:${tid}`,
            kind: 'MAIL',
            state: 'OPEN' as InboxState,
            title: t.subject,
            ...(last?.text ? { summary: last.text } : {}),
            priority: 'LOW',
            threadId: tid,
            createdAt: last?.at ?? this.now(),
          };
        })
      : [];
    return [...mail, ...work];
  }

  async transition(id: string, state: InboxState): Promise<void> {
    const item = this.items.find((i) => i.id === id);
    if (item) item.state = state;
  }

  async mailHistory(threadId: string): Promise<MailMessage[]> {
    return [...(this.threads.get(threadId)?.messages ?? [])];
  }

  async mailReply(threadId: string, content: string, attachment?: MailAttachment): Promise<void> {
    const t = this.threads.get(threadId);
    if (t) t.messages = [...t.messages, { id: `r_${this.seq++}`, actorId: 'me', kind: 'MESSAGE', at: this.now(), html: null, text: content || null, attachment: attachment ?? null }];
  }

  async uploadAttachment(file: File): Promise<MailAttachment> {
    return { contentRef: `mock/${this.seq++}`, mimeType: file.type || 'application/octet-stream', sizeBytes: file.size, filename: file.name };
  }

  async downloadAttachment(attachment: MailAttachment): Promise<string> {
    // Demo: no real bytes — hand back a data URL so the click resolves without a network call.
    return `data:${attachment.mimeType};base64,`;
  }

  async directory(): Promise<MailPerson[]> {
    return [...PEOPLE];
  }

  async composeInternal(recipientUserId: string, subject: string, text: string, attachments?: MailAttachment[]): Promise<void> {
    const threadId = `mt_${this.seq++}`;
    this.threads.set(threadId, { subject, messages: [{ id: `m_${this.seq++}`, actorId: 'me', kind: 'EMAIL', at: this.now(), html: `<p>${text}</p>`, text, attachment: attachments?.[0] ?? null }] });
    this.mailFold.unshift(threadId);
    void recipientUserId;
  }

  async composeExternal(target: string, subject: string, text: string, attachments?: MailAttachment[]): Promise<void> {
    // A mock external send has no in-app thread — no-op beyond acknowledging.
    void target;
    void subject;
    void text;
    void attachments;
  }
}
