"use client";

import { useCallback, useEffect, useState } from "react";
import { useApi } from "@/lib/useApi";

type Merchant = {
  id: number;
  name: string;
};

type CategoryMapping = {
  id: number;
  merchantId: number;
  merchantName: string;
  rawCategoryPath: string;
  partCategoryId: number | null;
  partCategoryName?: string | null;
};

// Matches the Spring MerchantCategoryMappingDto record
type MerchantCategoryMappingDto = {
  id: number;
  merchantId: number;
  merchantName: string;
  rawCategoryPath: string;
  partCategoryId: number | null;
  partCategoryName?: string | null;
};

type AdminCategory = {
  id: number;
  name: string;
  groupName?: string | null;
};

export default function MerchantCategoryMappingPage() {
  const { get, post } = useApi();

  const [merchants, setMerchants] = useState<Merchant[]>([]);
  const [selectedMerchantId, setSelectedMerchantId] = useState<number | null>(
    null
  );
  const [mappings, setMappings] = useState<CategoryMapping[]>([]);
  const [loadingMerchants, setLoadingMerchants] = useState(false);
  const [loadingMappings, setLoadingMappings] = useState(false);
  const [savingId, setSavingId] = useState<number | null>(null);
  const [error, setError] = useState<string | null>(null);
  const [categories, setCategories] = useState<AdminCategory[]>([]);
  const [loadingCategories, setLoadingCategories] = useState(false);
  const [dirty, setDirty] = useState<Record<number, CategoryMapping>>({});

  // 1) Load merchants for the dropdown
  useEffect(() => {
    setLoadingMerchants(true);
    setError(null);

    get<Merchant[]>("/api/admin/category-mappings/merchants")
      .then((data) => {
        setMerchants(data);
        if (data.length > 0 && selectedMerchantId == null) {
          setSelectedMerchantId(data[0].id);
        }
      })
      .catch((err: any) => {
        console.error("Failed to load merchants", err);
        setError(
          err instanceof Error ? err.message : "Failed to load merchants"
        );
      })
      .finally(() => setLoadingMerchants(false));
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  // Load global (canonical) part categories for the dropdown
  // Load global (canonical) part categories for the dropdown
  useEffect(() => {
    setLoadingCategories(true);
    setError(null);

    get<AdminCategory[]>("/api/admin/categories")
      .then((data) => {
        setCategories(data);
      })
      .catch((err: any) => {
        console.error("Failed to load categories", err);
        setError(
          err instanceof Error ? err.message : "Failed to load categories"
        );
      })
      .finally(() => setLoadingCategories(false));
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);
  // 2) Load mappings when merchant changes (THIS sends merchantId)
  const loadMappings = useCallback(() => {
    if (selectedMerchantId == null) return;

    setLoadingMappings(true);
    setError(null);

    get<CategoryMapping[]>(
      `/api/admin/category-mappings?merchantId=${selectedMerchantId}`
    )
      .then((data) => {
        setMappings(data);
      })
      .catch((err: any) => {
        console.error("Failed to load category mappings", err);
        setError(
          err instanceof Error
            ? err.message
            : "Failed to load category mappings"
        );
      })
      .finally(() => setLoadingMappings(false));
  }, [selectedMerchantId, get]);

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

  // 3) Save a single mapping row (adjust endpoint if yours differs)
  const handleSaveMapping = async (
    mappingId: number,
    updated: { partCategoryId: number | null }
  ) => {
    setSavingId(mappingId);
    setError(null);
    try {
      const updatedMapping = await post<MerchantCategoryMappingDto>(
        `/api/admin/category-mappings/${mappingId}`,
        {
          partCategoryId: updated.partCategoryId,
        }
      );

      setMappings((prev) =>
        prev.map((m) =>
          m.id === mappingId
            ? {
                ...m,
                // map Java DTO fields back into your UI model
                merchantId: updatedMapping.merchantId,
                merchantName: updatedMapping.merchantName,
                rawCategoryPath: updatedMapping.rawCategoryPath,
                partCategoryId: updatedMapping.partCategoryId,
                partCategoryName: updatedMapping.partCategoryName,
              }
            : m
        )
      );
    } catch (err: any) {
      console.error("Failed to save mapping", err);
      setError(err instanceof Error ? err.message : "Failed to save mapping");
    } finally {
      setSavingId(null);
    }
  };

  const handleSaveAll = async () => {
    if (Object.keys(dirty).length === 0) return;

    // special flag to indicate a global save is in progress
    setSavingId(-1);
    setError(null);

    try {
      const dirtyRows = Object.values(dirty);

      const updatedMappings = await Promise.all(
        dirtyRows.map((row) =>
          post<MerchantCategoryMappingDto>(
            `/api/admin/category-mappings/${row.id}`,
            {
              partCategoryId: row.partCategoryId ?? null,
            }
          )
        )
      );

      setMappings((prev) =>
        prev.map((m) => {
          const updated = updatedMappings.find((u) => u.id === m.id);
          if (!updated) return m;

          return {
            ...m,
            merchantId: updated.merchantId,
            merchantName: updated.merchantName,
            rawCategoryPath: updated.rawCategoryPath,
            partCategoryId: updated.partCategoryId,
            partCategoryName: updated.partCategoryName,
          };
        })
      );

      // Clear dirty state after successful batch save
      setDirty({});
    } catch (err: any) {
      console.error("Failed to save all mappings", err);
      setError(
        err instanceof Error
          ? err.message
          : "Failed to save all mappings"
      );
    } finally {
      setSavingId(null);
    }
  };

  return (
    <div className="p-6 space-y-6">
      <div className="flex items-center justify-between">
        <h1 className="text-2xl font-semibold text-zinc-50">
          Merchant Category Mapping
        </h1>

        <div className="flex items-center gap-2">
          <span className="text-sm text-zinc-400">Merchant:</span>
          <select
            className="bg-zinc-900 border border-zinc-700 rounded px-2 py-1 text-sm"
            value={selectedMerchantId ?? ""}
            onChange={(e) =>
              setSelectedMerchantId(
                e.target.value ? Number(e.target.value) : null
              )
            }
          >
            {loadingMerchants && <option value="">Loading merchants…</option>}
            {!loadingMerchants && merchants.length === 0 && (
              <option value="">No merchants</option>
            )}
            {merchants.map((m) => (
              <option key={m.id} value={m.id}>
                {m.name}
              </option>
            ))}
          </select>
        </div>
      </div>

      {error && (
        <div className="rounded bg-red-900/40 border border-red-700 px-3 py-2 text-sm text-red-200">
          {error}
        </div>
      )}

      {Object.keys(dirty).length > 0 && (
        <div className="flex justify-end">
          <button
            onClick={handleSaveAll}
            disabled={savingId !== null}
            className="mb-3 rounded bg-emerald-600 px-4 py-2 text-sm font-medium text-white hover:bg-emerald-500 disabled:opacity-50"
          >
            Save All Changes ({Object.keys(dirty).length})
          </button>
        </div>
      )}

      <div className="border border-zinc-800 rounded-lg overflow-hidden">
        <div className="bg-zinc-900 border-b border-zinc-800 px-4 py-2 text-xs uppercase tracking-wide text-zinc-400">
          Category Mappings
        </div>

        {loadingMappings ? (
          <div className="p-4 text-sm text-zinc-400">Loading mappings…</div>
        ) : mappings.length === 0 ? (
          <div className="p-4 text-sm text-zinc-400">
            No mappings found for this merchant.
          </div>
        ) : (
          <table className="w-full text-sm">
            <thead className="bg-zinc-950/60 border-b border-zinc-800">
              <tr>
                <th className="text-left px-4 py-2 text-xs uppercase tracking-wide text-zinc-500">
                  Merchant Category
                </th>
                <th className="text-left px-4 py-2 text-xs uppercase tracking-wide text-zinc-500">
                  Global Category
                </th>
                <th className="px-4 py-2 text-xs uppercase tracking-wide text-zinc-500">
                  Actions
                </th>
              </tr>
            </thead>
            <tbody>
              {mappings.map((m) => (
                <tr key={m.id} className="border-t border-zinc-800">
                  <td className="px-4 py-2 text-zinc-100">
                    {m.rawCategoryPath}
                  </td>
                  <td className="px-4 py-2">
                    {loadingCategories ? (
                      <span className="text-xs text-zinc-500">
                        Loading categories…
                      </span>
                    ) : categories.length === 0 ? (
                      <span className="text-xs text-zinc-500">
                        No categories
                      </span>
                    ) : (
                      <select
                        className="bg-zinc-950 border border-zinc-700 rounded px-2 py-1 text-sm w-64"
                        value={m.partCategoryId ?? ""}
                        onChange={(e) => {
                          const value = e.target.value
                            ? Number(e.target.value)
                            : null;

                          const updatedRow: CategoryMapping = {
                            ...m,
                            partCategoryId: value,
                          };

                          setMappings((prev) =>
                            prev.map((row) =>
                              row.id === m.id ? updatedRow : row
                            )
                          );

                          // mark this row as dirty so it can be batch-saved
                          setDirty((prev) => ({
                            ...prev,
                            [m.id]: updatedRow,
                          }));
                        }}
                      >
                        <option value="">— Unmapped —</option>
                        {categories.map((cat) => (
                          <option key={cat.id} value={cat.id}>
                            {cat.groupName ? `[${cat.groupName}] ` : ""}
                            {cat.name}
                          </option>
                        ))}
                      </select>
                    )}

                    {m.partCategoryName && (
                      <div className="text-xs text-zinc-500 mt-1">
                        Current: {m.partCategoryName}
                      </div>
                    )}
                  </td>
                  <td className="px-4 py-2 text-right">
                    <button
                      onClick={() =>
                        handleSaveMapping(m.id, {
                          partCategoryId: m.partCategoryId ?? null,
                        })
                      }
                      disabled={savingId === m.id}
                      className="text-xs px-3 py-1 rounded border border-zinc-600 hover:bg-zinc-800 disabled:opacity-50"
                    >
                      {savingId === m.id ? "Saving…" : "Save"}
                    </button>
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        )}
      </div>
    </div>
  );
}
