import { useEffect, useRef, useCallback, useState } from "react";
import { useTranscriptionsStore } from "../../stores/transcriptions-store";
import { useConfigStore } from "../../stores/config-store";
import { useT } from "../../i18n-context";
import { computeLlmConfigHash } from "../../../shared/llm-config-hash";
import type { TranscriptionEntry } from "../../../shared/types";
import {
  SearchIcon,
  CheckIcon,
  CopyIcon,
  TrashAltIcon,
  ChevronsLeftIcon,
  ChevronLeftIcon,
  ChevronRightIcon,
  ChevronsRightIcon,
  InfoCircleIcon,
  PlayIcon,
  PauseIcon,
  DownloadIcon,
  RefreshIcon,
  AlertTriangleIcon,
  EllipsisVerticalIcon,
} from "../../../shared/icons";
import { CustomSelect } from "../ui/CustomSelect";
import card from "../shared/card.module.scss";
import styles from "./TranscriptionsPanel.module.scss";

const DEBOUNCE_MS = 300;

function formatTime(timestamp: string): string {
  return new Date(timestamp).toLocaleTimeString(undefined, { hour: "numeric", minute: "2-digit" });
}

function formatDuration(ms: number): string {
  const seconds = ms / 1000;
  if (seconds < 60) return `${seconds.toFixed(1)}s`;
  const minutes = Math.floor(seconds / 60);
  const remainingSeconds = seconds % 60;
  return `${minutes}m ${remainingSeconds.toFixed(0)}s`;
}

function formatDateGroup(timestamp: string, t: (key: string) => string): string {
  const date = new Date(timestamp);
  const now = new Date();

  if (date.toDateString() === now.toDateString()) return t("history.today");

  const yesterday = new Date(now);
  yesterday.setDate(yesterday.getDate() - 1);
  if (date.toDateString() === yesterday.toDateString()) return t("history.yesterday");

  return date.toLocaleDateString(undefined, { month: "long", day: "numeric" });
}

function groupByDate(entries: TranscriptionEntry[], t: (key: string) => string): Map<string, TranscriptionEntry[]> {
  const groups = new Map<string, TranscriptionEntry[]>();
  for (const entry of entries) {
    const key = formatDateGroup(entry.timestamp, t);
    const group = groups.get(key) ?? [];
    group.push(entry);
    groups.set(key, group);
  }
  return groups;
}

function CopyButton({ text, t }: { text: string; t: (key: string) => string }) {
  const [copied, setCopied] = useState(false);

  const handleCopy = async () => {
    await window.voxApi.clipboard.write(text);
    setCopied(true);
    setTimeout(() => setCopied(false), 2000);
  };

  return (
    <button className={styles.copyButton} onClick={handleCopy} title={copied ? t("history.copied") : t("history.copyToClipboard")}>
      {copied ? (
        <CheckIcon width={14} height={14} />
      ) : (
        <CopyIcon width={14} height={14} />
      )}
    </button>
  );
}

function AudioPlayer({ entryId }: { entryId: string }) {
  const t = useT();
  const [playing, setPlaying] = useState(false);
  const audioRef = useRef<HTMLAudioElement | null>(null);

  const togglePlay = useCallback(async () => {
    if (playing && audioRef.current) {
      audioRef.current.pause();
      setPlaying(false);
      return;
    }

    if (!audioRef.current) {
      const dataUrl = await window.voxApi.history.getAudioDataUrl(entryId);
      if (!dataUrl) return;
      audioRef.current = new Audio(dataUrl);
      audioRef.current.onended = () => setPlaying(false);
    }
    await audioRef.current.play();
    setPlaying(true);
  }, [entryId, playing]);

  useEffect(() => {
    return () => { audioRef.current?.pause(); };
  }, []);

  return (
    <button
      className={`${styles.audioButton} ${playing ? styles.audioButtonPlaying : ""}`}
      onClick={togglePlay}
      title={playing ? t("history.pause") : t("history.play")}
    >
      {playing ? <PauseIcon width={14} height={14} /> : <PlayIcon width={14} height={14} />}
    </button>
  );
}

function OverflowMenu({
  entry,
  retryEntry,
  retryingId,
  deleteEntry,
}: {
  entry: TranscriptionEntry;
  retryEntry: (id: string) => Promise<void>;
  retryingId: string | null;
  deleteEntry: (id: string) => void;
}) {
  const t = useT();
  const config = useConfigStore((s) => s.config);
  const isRetrying = retryingId === entry.id;
  const [downloaded, setDownloaded] = useState(false);

  const retryBlockedByLlm = entry.failedStep === "llm"
    && config?.enableLlmEnhancement === true
    && (!config.llmConnectionTested || computeLlmConfigHash(config) !== config.llmConfigHash);

  return (
    <div className={styles.overflowMenu}>
      <button
        className={styles.overflowTrigger}
      >
        <EllipsisVerticalIcon width={14} height={14} />
      </button>
      <div className={styles.overflowDropdown}>
        <div className={styles.overflowDropdownInner}>
          {entry.audioFilePath && (
            <button
              className={styles.overflowItem}
              onClick={() => retryEntry(entry.id)}
              disabled={isRetrying || retryingId !== null || retryBlockedByLlm}
              title={retryBlockedByLlm ? t("ui.llmConfigBanner") : undefined}
            >
              <RefreshIcon width={14} height={14} />
              <span>{isRetrying ? t("history.retrying") : t("history.retry")}</span>
            </button>
          )}
          {entry.audioFilePath && (
            <button
              className={styles.overflowItem}
              onClick={async () => {
                await window.voxApi.history.downloadAudio(entry.id);
                setDownloaded(true);
                setTimeout(() => setDownloaded(false), 2000);
              }}
            >
              {downloaded ? <CheckIcon width={14} height={14} /> : <DownloadIcon width={14} height={14} />}
              <span>{downloaded ? t("history.downloaded") : t("history.download")}</span>
            </button>
          )}
          <button
            className={`${styles.overflowItem} ${styles.overflowItemDanger}`}
            onClick={() => deleteEntry(entry.id)}
          >
            <TrashAltIcon width={14} height={14} />
            <span>{t("history.deleteTranscription")}</span>
          </button>
        </div>
      </div>
    </div>
  );
}

export function TranscriptionsPanel() {
  const t = useT();
  const config = useConfigStore((s) => s.config);
  const copyToClipboard = config?.copyToClipboard;
  const entries = useTranscriptionsStore((s) => s.entries);
  const total = useTranscriptionsStore((s) => s.total);
  const page = useTranscriptionsStore((s) => s.page);
  const pageSize = useTranscriptionsStore((s) => s.pageSize);
  const loading = useTranscriptionsStore((s) => s.loading);
  const searchQuery = useTranscriptionsStore((s) => s.searchQuery);
  const fetchPage = useTranscriptionsStore((s) => s.fetchPage);
  const setPage = useTranscriptionsStore((s) => s.setPage);
  const setPageSize = useTranscriptionsStore((s) => s.setPageSize);
  const search = useTranscriptionsStore((s) => s.search);
  const deleteEntry = useTranscriptionsStore((s) => s.deleteEntry);
  const clearHistory = useTranscriptionsStore((s) => s.clearHistory);
  const retryEntry = useTranscriptionsStore((s) => s.retryEntry);
  const retryingId = useTranscriptionsStore((s) => s.retryingId);

  const debounceRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
  const searchInputRef = useRef<HTMLInputElement>(null);
  const [confirmClear, setConfirmClear] = useState(false);

  const totalPages = Math.ceil(total / pageSize);

  useEffect(() => {
    searchInputRef.current?.focus();
  }, []);

  useEffect(() => {
    fetchPage();
  }, [fetchPage]);

  useEffect(() => {
    return window.voxApi.history.onEntryAdded(() => {
      fetchPage();
    });
  }, [fetchPage]);

  const handleSearchChange = useCallback(
    (e: React.ChangeEvent<HTMLInputElement>) => {
      const value = e.target.value;
      clearTimeout(debounceRef.current);
      debounceRef.current = setTimeout(() => {
        search(value);
      }, DEBOUNCE_MS);
    },
    [search],
  );

  const handleClear = async () => {
    if (!confirmClear) {
      setConfirmClear(true);
      return;
    }
    await clearHistory();
    setConfirmClear(false);
  };

  const groups = groupByDate(entries, t);

  return (
    <>
      <div className={card.card}>
        <div className={card.header}>
          <h2>{t("history.title")}</h2>
          <p className={card.description}>
            {total > 0 ? (total === 1 ? t("history.oneStored") : t("history.countStored", { count: total })) : t("history.emptyState")}
          </p>
        </div>
        {copyToClipboard && (
          <div className={card.infoBanner}>
            <InfoCircleIcon width={14} height={14} />
            <span>{t("history.clipboardHint")}</span>
          </div>
        )}
        <div className={card.body}>
          <div className={styles.searchContainer}>
            <SearchIcon className={styles.searchIcon} width={16} height={16} />
            <input
              ref={searchInputRef}
              type="text"
              placeholder={t("history.searchPlaceholder")}
              defaultValue={searchQuery}
              onChange={handleSearchChange}
              className={styles.searchInput}
            />
          </div>

          {entries.length === 0 && !loading ? (
            <div className={styles.emptyState}>
              {searchQuery ? t("history.noSearchResults") : t("history.noTranscriptions")}
            </div>
          ) : (
            <div className={styles.entryList}>
              {Array.from(groups.entries()).map(([dateLabel, groupEntries]) => (
                <div key={dateLabel} className={styles.dateGroup}>
                  <div className={styles.dateHeader}>{dateLabel}</div>
                  {groupEntries.map((entry) => {
                    const entryClass = [
                      styles.entry,
                      entry.status === "whisper_failed" ? styles.entryWhisperFailed : "",
                      entry.status === "llm_failed" ? styles.entryLlmFailed : "",
                      retryingId === entry.id ? styles.entryRetrying : "",
                    ].filter(Boolean).join(" ");

                    return (
                      <div key={entry.id} className={entryClass}>
                        <div className={styles.entryContent}>
                          {entry.status === "whisper_failed" ? (
                            <>
                              <p className={styles.errorText}>
                                {t("history.status.whisperFailed")}
                              </p>
                              {entry.errorMessage && (
                                <p className={styles.errorText}>{entry.errorMessage}</p>
                              )}
                            </>
                          ) : (
                            <p className={styles.entryText}>{entry.text}</p>
                          )}
                          <div className={styles.entryMeta}>
                            <span>{formatTime(entry.timestamp)}</span>
                            {entry.status !== "whisper_failed" && (
                              <span>{t("history.words", { count: entry.wordCount })}</span>
                            )}
                            <span>{formatDuration(entry.audioDurationMs)}</span>
                            <span>{entry.whisperModel}</span>
                            {entry.llmEnhanced && entry.llmProvider && (
                              <span className={styles.badge}>{entry.llmProvider}</span>
                            )}
                            {entry.status === "llm_failed" && (
                              <span className={`${styles.statusBadge} ${styles.statusBadgeWarning}`}>
                                <AlertTriangleIcon width={12} height={12} />
                                {t("history.status.llmFailed")}
                              </span>
                            )}
                            {entry.status === "whisper_failed" && (
                              <span className={`${styles.statusBadge} ${styles.statusBadgeError}`}>
                                <AlertTriangleIcon width={12} height={12} />
                                {t("history.status.whisperFailed")}
                              </span>
                            )}
                          </div>
                        </div>
                        <div className={styles.entryActions}>
                          {entry.audioFilePath && (
                            <AudioPlayer entryId={entry.id} />
                          )}
                          {entry.status !== "whisper_failed" && (
                            <CopyButton text={entry.text} t={t} />
                          )}
                          <OverflowMenu
                            entry={entry}
                            retryEntry={retryEntry}
                            retryingId={retryingId}
                            deleteEntry={deleteEntry}
                          />
                        </div>
                      </div>
                    );
                  })}
                </div>
              ))}
            </div>
          )}

          {loading && <div className={styles.spinner}>{t("history.loading")}</div>}

          {total > 0 && (
            <div className={styles.pagination}>
              <div className={styles.pageInfo}>
                {/* eslint-disable-next-line i18next/no-literal-string */}
                {page} / {totalPages || 1}
              </div>
              <div className={styles.pageControls}>
                {page > 2 && (
                  <button onClick={() => setPage(1)}>
                    <ChevronsLeftIcon width={12} height={12} />
                  </button>
                )}
                <button disabled={page <= 1} onClick={() => setPage(page - 1)}>
                  <ChevronLeftIcon width={12} height={12} />
                </button>
                <button disabled={page >= totalPages} onClick={() => setPage(page + 1)}>
                  <ChevronRightIcon width={12} height={12} />
                </button>
                {page < totalPages - 1 && (
                  <button onClick={() => setPage(totalPages)}>
                    <ChevronsRightIcon width={12} height={12} />
                  </button>
                )}
              </div>
              <div className={styles.pageSizeSelect}>
                <CustomSelect
                  value={String(pageSize)}
                  items={[
                    { value: "10", label: "10 / page" },
                    { value: "25", label: "25 / page" },
                    { value: "50", label: "50 / page" },
                  ]}
                  onChange={(value) => setPageSize(Number(value))}
                />
              </div>
            </div>
          )}
        </div>
      </div>

      {total > 0 && (
        <div className={card.card}>
          <div className={card.body}>
            <button
              className={`${styles.clearButton} ${confirmClear ? styles.confirmClear : ""}`}
              onClick={handleClear}
              onBlur={() => setConfirmClear(false)}
            >
              {confirmClear ? t("history.confirmClear") : t("history.clearHistory")}
            </button>
          </div>
        </div>
      )}
    </>
  );
}
