import { useCallback, useEffect, useState } from 'react';
import { useInboxAdapter } from './provider';
import type { InboxItem, InboxState, MailAttachment, MailMessage, MailPerson } from './types';

export interface InboxData {
  items: InboxItem[];
  loading: boolean;
  error: string | null;
  transition: (id: string, state: InboxState) => Promise<void>;
  refetch: () => void;
}

/** The unified inbox for a given filter state. Refetches when the filter changes. */
export function useInbox(state?: InboxState): InboxData {
  const adapter = useInboxAdapter();
  const [items, setItems] = useState<InboxItem[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [nonce, setNonce] = useState(0);

  useEffect(() => {
    let alive = true;
    setLoading(true);
    adapter
      .listInbox(state)
      .then((list) => {
        if (!alive) return;
        setItems(list);
        setError(null);
      })
      .catch((e: unknown) => {
        if (!alive) return;
        setError(e instanceof Error ? e.message : String(e));
        setItems([]);
      })
      .finally(() => {
        if (alive) setLoading(false);
      });
    return () => {
      alive = false;
    };
  }, [adapter, state, nonce]);

  const refetch = useCallback(() => setNonce((n) => n + 1), []);
  const transition = useCallback(
    async (id: string, next: InboxState) => {
      await adapter.transition(id, next);
      setNonce((n) => n + 1);
    },
    [adapter],
  );

  return { items, loading, error, transition, refetch };
}

export interface MailThreadState {
  messages: MailMessage[];
  loading: boolean;
  error: string | null;
  reply: (content: string, attachment?: MailAttachment) => Promise<void>;
  canAttach: boolean;
  upload: (file: File) => Promise<MailAttachment>;
  canDownload: boolean;
  download: (attachment: MailAttachment) => Promise<string>;
  refetch: () => void;
}

/** One mail thread: history + reply. */
export function useMailThread(threadId: string | null): MailThreadState {
  const adapter = useInboxAdapter();
  const [messages, setMessages] = useState<MailMessage[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [nonce, setNonce] = useState(0);

  useEffect(() => {
    if (!threadId) {
      setMessages([]);
      setLoading(false);
      return;
    }
    let alive = true;
    setLoading(true);
    adapter
      .mailHistory(threadId)
      .then((m) => {
        if (!alive) return;
        setMessages(m);
        setError(null);
      })
      .catch((e: unknown) => {
        if (alive) setError(e instanceof Error ? e.message : String(e));
      })
      .finally(() => {
        if (alive) setLoading(false);
      });
    return () => {
      alive = false;
    };
  }, [adapter, threadId, nonce]);

  const refetch = useCallback(() => setNonce((n) => n + 1), []);
  const reply = useCallback(
    async (content: string, attachment?: MailAttachment) => {
      if (!threadId) return;
      await adapter.mailReply(threadId, content, attachment);
      setNonce((n) => n + 1);
    },
    [adapter, threadId],
  );
  const upload = useCallback(
    async (file: File) => {
      if (!adapter.uploadAttachment) throw new Error('attachments are not supported by this adapter');
      return adapter.uploadAttachment(file);
    },
    [adapter],
  );
  const download = useCallback(
    async (attachment: MailAttachment) => {
      if (!adapter.downloadAttachment) throw new Error('attachment download is not supported by this adapter');
      return adapter.downloadAttachment(attachment);
    },
    [adapter],
  );

  return {
    messages,
    loading,
    error,
    reply,
    canAttach: typeof adapter.uploadAttachment === 'function',
    upload,
    canDownload: typeof adapter.downloadAttachment === 'function',
    download,
    refetch,
  };
}

export interface ComposeState {
  supported: boolean;
  directory: MailPerson[];
  sendInternal: (recipientUserId: string, subject: string, text: string, attachments?: MailAttachment[]) => Promise<void>;
  sendExternal: (target: string, subject: string, text: string, attachments?: MailAttachment[]) => Promise<void>;
  canAttach: boolean;
  upload: (file: File) => Promise<MailAttachment>;
}

/** Compose a new message — in-app (to a person) or external (to an email). */
export function useCompose(): ComposeState {
  const adapter = useInboxAdapter();
  const supported = typeof adapter.composeInternal === 'function';
  const [directory, setDirectory] = useState<MailPerson[]>([]);

  useEffect(() => {
    if (!adapter.directory) return;
    let alive = true;
    adapter
      .directory()
      .then((d) => {
        if (alive) setDirectory(d);
      })
      .catch(() => {
        if (alive) setDirectory([]);
      });
    return () => {
      alive = false;
    };
  }, [adapter]);

  const sendInternal = useCallback(
    async (recipientUserId: string, subject: string, text: string, attachments?: MailAttachment[]) => {
      if (!adapter.composeInternal) throw new Error('compose is not supported by this adapter');
      await adapter.composeInternal(recipientUserId, subject, text, attachments);
    },
    [adapter],
  );
  const sendExternal = useCallback(
    async (target: string, subject: string, text: string, attachments?: MailAttachment[]) => {
      if (!adapter.composeExternal) throw new Error('compose is not supported by this adapter');
      await adapter.composeExternal(target, subject, text, attachments);
    },
    [adapter],
  );
  const upload = useCallback(
    async (file: File) => {
      if (!adapter.uploadAttachment) throw new Error('attachments are not supported by this adapter');
      return adapter.uploadAttachment(file);
    },
    [adapter],
  );

  return { supported, directory, sendInternal, sendExternal, canAttach: typeof adapter.uploadAttachment === 'function', upload };
}
