"use client";

import { useState, useMemo, useEffect, useCallback } from "react";
import { useSearchParams } from "next/navigation";
import { PageLayout, LoadingState, PageHeader } from "@/components/layout";
import {
  TransactionFilters,
  TransactionBulkActions,
  TransactionTable,
} from "@/components/transactions";
import { RuleCreateDialog } from "@/components/rules";
import { OFXImportDialog } from "@/components/import/ofx-import-dialog";
import { useBankingData } from "@/lib/hooks";
import { updateCategory, updateTransaction } from "@/lib/store-db";
import { Button } from "@/components/ui/button";
import { Upload } from "lucide-react";
import type { Transaction } from "@/lib/types";
import {
  normalizeDescription,
  suggestKeyword,
} from "@/components/rules/constants";

type SortField = "date" | "amount" | "description";
type SortOrder = "asc" | "desc";

export default function TransactionsPage() {
  const searchParams = useSearchParams();
  const { data, isLoading, refresh, update } = useBankingData();
  const [searchQuery, setSearchQuery] = useState("");
  const [selectedAccounts, setSelectedAccounts] = useState<string[]>(["all"]);

  useEffect(() => {
    const accountId = searchParams.get("accountId");
    if (accountId) {
      setSelectedAccounts([accountId]);
    }
  }, [searchParams]);

  const [selectedCategories, setSelectedCategories] = useState<string[]>(["all"]);
  const [showReconciled, setShowReconciled] = useState<string>("all");
  const [sortField, setSortField] = useState<SortField>("date");
  const [sortOrder, setSortOrder] = useState<SortOrder>("desc");
  const [selectedTransactions, setSelectedTransactions] = useState<Set<string>>(
    new Set()
  );
  const [ruleDialogOpen, setRuleDialogOpen] = useState(false);
  const [ruleTransaction, setRuleTransaction] = useState<Transaction | null>(null);

  // Transactions filtered for account filter (by categories, search, reconciled - not accounts)
  const transactionsForAccountFilter = useMemo(() => {
    if (!data) return [];

    let transactions = [...data.transactions];

    if (searchQuery) {
      const query = searchQuery.toLowerCase();
      transactions = transactions.filter(
        (t) =>
          t.description.toLowerCase().includes(query) ||
          t.memo?.toLowerCase().includes(query)
      );
    }

    if (!selectedCategories.includes("all")) {
      if (selectedCategories.includes("uncategorized")) {
        transactions = transactions.filter((t) => !t.categoryId);
      } else {
        transactions = transactions.filter(
          (t) => t.categoryId && selectedCategories.includes(t.categoryId)
        );
      }
    }

    if (showReconciled !== "all") {
      const isReconciled = showReconciled === "reconciled";
      transactions = transactions.filter(
        (t) => t.isReconciled === isReconciled
      );
    }

    return transactions;
  }, [data, searchQuery, selectedCategories, showReconciled]);

  // Transactions filtered for category filter (by accounts, search, reconciled - not categories)
  const transactionsForCategoryFilter = useMemo(() => {
    if (!data) return [];

    let transactions = [...data.transactions];

    if (searchQuery) {
      const query = searchQuery.toLowerCase();
      transactions = transactions.filter(
        (t) =>
          t.description.toLowerCase().includes(query) ||
          t.memo?.toLowerCase().includes(query)
      );
    }

    if (!selectedAccounts.includes("all")) {
      transactions = transactions.filter(
        (t) => selectedAccounts.includes(t.accountId)
      );
    }

    if (showReconciled !== "all") {
      const isReconciled = showReconciled === "reconciled";
      transactions = transactions.filter(
        (t) => t.isReconciled === isReconciled
      );
    }

    return transactions;
  }, [data, searchQuery, selectedAccounts, showReconciled]);

  const filteredTransactions = useMemo(() => {
    if (!data) return [];

    let transactions = [...data.transactions];

    if (searchQuery) {
      const query = searchQuery.toLowerCase();
      transactions = transactions.filter(
        (t) =>
          t.description.toLowerCase().includes(query) ||
          t.memo?.toLowerCase().includes(query)
      );
    }

    if (!selectedAccounts.includes("all")) {
      transactions = transactions.filter(
        (t) => selectedAccounts.includes(t.accountId)
      );
    }

    if (!selectedCategories.includes("all")) {
      if (selectedCategories.includes("uncategorized")) {
        transactions = transactions.filter((t) => !t.categoryId);
      } else {
        transactions = transactions.filter(
          (t) => t.categoryId && selectedCategories.includes(t.categoryId)
        );
      }
    }

    if (showReconciled !== "all") {
      const isReconciled = showReconciled === "reconciled";
      transactions = transactions.filter(
        (t) => t.isReconciled === isReconciled
      );
    }

    transactions.sort((a, b) => {
      let comparison = 0;
      switch (sortField) {
        case "date":
          comparison = new Date(a.date).getTime() - new Date(b.date).getTime();
          break;
        case "amount":
          comparison = a.amount - b.amount;
          break;
        case "description":
          comparison = a.description.localeCompare(b.description);
          break;
      }
      return sortOrder === "asc" ? comparison : -comparison;
    });

    return transactions;
  }, [
    data,
    searchQuery,
    selectedAccounts,
    selectedCategories,
    showReconciled,
    sortField,
    sortOrder,
  ]);

  const handleCreateRule = useCallback((transaction: Transaction) => {
    setRuleTransaction(transaction);
    setRuleDialogOpen(true);
  }, []);

  // Create a virtual group for the rule dialog based on selected transaction
  const ruleGroup = useMemo(() => {
    if (!ruleTransaction || !data) return null;

    // Find similar transactions (same normalized description)
    const normalizedDesc = normalizeDescription(ruleTransaction.description);
    const similarTransactions = data.transactions.filter(
      (t) => normalizeDescription(t.description) === normalizedDesc
    );

    return {
      key: normalizedDesc,
      displayName: ruleTransaction.description,
      transactions: similarTransactions,
      totalAmount: similarTransactions.reduce((sum, t) => sum + t.amount, 0),
      suggestedKeyword: suggestKeyword(similarTransactions.map((t) => t.description)),
    };
  }, [ruleTransaction, data]);

  const handleSaveRule = useCallback(
    async (ruleData: {
      keyword: string;
      categoryId: string;
      applyToExisting: boolean;
      transactionIds: string[];
    }) => {
      if (!data) return;

      // 1. Add keyword to category
      const category = data.categories.find((c) => c.id === ruleData.categoryId);
      if (!category) {
        throw new Error("Category not found");
      }

      // Check if keyword already exists
      const keywordExists = category.keywords.some(
        (k) => k.toLowerCase() === ruleData.keyword.toLowerCase()
      );

      if (!keywordExists) {
        await updateCategory({
          ...category,
          keywords: [...category.keywords, ruleData.keyword],
        });
      }

      // 2. Apply to existing transactions if requested
      if (ruleData.applyToExisting) {
        const transactions = data.transactions.filter((t) =>
          ruleData.transactionIds.includes(t.id)
        );

        await Promise.all(
          transactions.map((t) =>
            updateTransaction({ ...t, categoryId: ruleData.categoryId })
          )
        );
      }

      refresh();
      setRuleDialogOpen(false);
    },
    [data, refresh]
  );

  if (isLoading || !data) {
    return <LoadingState />;
  }

  const formatCurrency = (amount: number) => {
    return new Intl.NumberFormat("fr-FR", {
      style: "currency",
      currency: "EUR",
    }).format(amount);
  };

  const formatDate = (dateStr: string) => {
    return new Date(dateStr).toLocaleDateString("fr-FR", {
      day: "2-digit",
      month: "short",
      year: "numeric",
    });
  };

  const toggleReconciled = async (transactionId: string) => {
    const transaction = data.transactions.find((t) => t.id === transactionId);
    if (!transaction) return;

    const updatedTransaction = {
      ...transaction,
      isReconciled: !transaction.isReconciled,
    };

    const updatedTransactions = data.transactions.map((t) =>
      t.id === transactionId ? updatedTransaction : t
    );
    update({ ...data, transactions: updatedTransactions });

    try {
      await fetch("/api/banking/transactions", {
        method: "PUT",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(updatedTransaction),
      });
    } catch (error) {
      console.error("Failed to update transaction:", error);
      refresh();
    }
  };

  const markReconciled = async (transactionId: string) => {
    const transaction = data.transactions.find((t) => t.id === transactionId);
    if (!transaction || transaction.isReconciled) return; // Skip if already reconciled

    const updatedTransaction = {
      ...transaction,
      isReconciled: true,
    };

    const updatedTransactions = data.transactions.map((t) =>
      t.id === transactionId ? updatedTransaction : t
    );
    update({ ...data, transactions: updatedTransactions });

    try {
      await fetch("/api/banking/transactions", {
        method: "PUT",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(updatedTransaction),
      });
    } catch (error) {
      console.error("Failed to update transaction:", error);
      refresh();
    }
  };

  const setCategory = async (
    transactionId: string,
    categoryId: string | null
  ) => {
    const transaction = data.transactions.find((t) => t.id === transactionId);
    if (!transaction) return;

    const updatedTransaction = { ...transaction, categoryId };

    const updatedTransactions = data.transactions.map((t) =>
      t.id === transactionId ? updatedTransaction : t
    );
    update({ ...data, transactions: updatedTransactions });

    try {
      await fetch("/api/banking/transactions", {
        method: "PUT",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(updatedTransaction),
      });
    } catch (error) {
      console.error("Failed to update transaction:", error);
      refresh();
    }
  };

  const bulkReconcile = async (reconciled: boolean) => {
    const transactionsToUpdate = data.transactions.filter((t) =>
      selectedTransactions.has(t.id)
    );

    const updatedTransactions = data.transactions.map((t) =>
      selectedTransactions.has(t.id) ? { ...t, isReconciled: reconciled } : t
    );
    update({ ...data, transactions: updatedTransactions });
    setSelectedTransactions(new Set());

    try {
      await Promise.all(
        transactionsToUpdate.map((t) =>
          fetch("/api/banking/transactions", {
            method: "PUT",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify({ ...t, isReconciled: reconciled }),
          })
        )
      );
    } catch (error) {
      console.error("Failed to update transactions:", error);
      refresh();
    }
  };

  const bulkSetCategory = async (categoryId: string | null) => {
    const transactionsToUpdate = data.transactions.filter((t) =>
      selectedTransactions.has(t.id)
    );

    const updatedTransactions = data.transactions.map((t) =>
      selectedTransactions.has(t.id) ? { ...t, categoryId } : t
    );
    update({ ...data, transactions: updatedTransactions });
    setSelectedTransactions(new Set());

    try {
      await Promise.all(
        transactionsToUpdate.map((t) =>
          fetch("/api/banking/transactions", {
            method: "PUT",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify({ ...t, categoryId }),
          })
        )
      );
    } catch (error) {
      console.error("Failed to update transactions:", error);
      refresh();
    }
  };

  const toggleSelectAll = () => {
    if (selectedTransactions.size === filteredTransactions.length) {
      setSelectedTransactions(new Set());
    } else {
      setSelectedTransactions(new Set(filteredTransactions.map((t) => t.id)));
    }
  };

  const toggleSelectTransaction = (id: string) => {
    const newSelected = new Set(selectedTransactions);
    if (newSelected.has(id)) {
      newSelected.delete(id);
    } else {
      newSelected.add(id);
    }
    setSelectedTransactions(newSelected);
  };

  const handleSortChange = (field: SortField) => {
    if (sortField === field) {
      setSortOrder(sortOrder === "asc" ? "desc" : "asc");
    } else {
      setSortField(field);
      setSortOrder(field === "date" ? "desc" : "asc");
    }
  };

  return (
    <PageLayout>
      <PageHeader
        title="Transactions"
        description={`${filteredTransactions.length} transaction${filteredTransactions.length > 1 ? "s" : ""}`}
        actions={
          <OFXImportDialog onImportComplete={refresh}>
            <Button>
              <Upload className="w-4 h-4 mr-2" />
              Importer OFX
            </Button>
          </OFXImportDialog>
        }
      />

      <TransactionFilters
        searchQuery={searchQuery}
        onSearchChange={setSearchQuery}
        selectedAccounts={selectedAccounts}
        onAccountsChange={setSelectedAccounts}
        selectedCategories={selectedCategories}
        onCategoriesChange={setSelectedCategories}
        showReconciled={showReconciled}
        onReconciledChange={setShowReconciled}
        accounts={data.accounts}
        folders={data.folders}
        categories={data.categories}
        transactionsForAccountFilter={transactionsForAccountFilter}
        transactionsForCategoryFilter={transactionsForCategoryFilter}
      />

      <TransactionBulkActions
        selectedCount={selectedTransactions.size}
        categories={data.categories}
        onReconcile={bulkReconcile}
        onSetCategory={bulkSetCategory}
      />

      <TransactionTable
        transactions={filteredTransactions}
        accounts={data.accounts}
        categories={data.categories}
        selectedTransactions={selectedTransactions}
        sortField={sortField}
        sortOrder={sortOrder}
        onSortChange={handleSortChange}
        onToggleSelectAll={toggleSelectAll}
        onToggleSelectTransaction={toggleSelectTransaction}
        onToggleReconciled={toggleReconciled}
        onMarkReconciled={markReconciled}
        onSetCategory={setCategory}
        onCreateRule={handleCreateRule}
        formatCurrency={formatCurrency}
        formatDate={formatDate}
      />

      <RuleCreateDialog
        open={ruleDialogOpen}
        onOpenChange={setRuleDialogOpen}
        group={ruleGroup}
        categories={data.categories}
        onSave={handleSaveRule}
      />
    </PageLayout>
  );
}
