import { useEffect, useMemo, useRef, useState } from 'react';
import { Search, ChevronRight } from 'lucide-react';
import { cn } from '../../lib/cn';
import { formatValue } from '../../lib/format';
import type { ZinoClient } from '../../api/client';
import type { RecordViewField, RecordViewResponse } from '../../api/types';
import { Pagination } from '../reusable/Pagination';
import { Spinner } from '../reusable/Spinner';
import { EmptyState } from '../reusable/EmptyState';
import { StatsTiles } from '../reusable/StatsTiles';
import { AnalyticsChart } from '../reusable/AnalyticsChart';
import { Select } from '../reusable/Select';

export interface RecordViewProps {
  /** Workflow-bound client (see api/clients.ts). */
  client: ZinoClient;
  /** rv_template_uid for this view. */
  rvUid: string;
  /** Card header title. */
  title?: string;
  /** Restrict/order visible columns by field_key. Defaults to all fields. */
  columns?: string[];
  /** Rows per page. @default 25 */
  pageSize?: number;
  /** Click handler — receives the raw row + index. */
  onRowClick?: (row: Record<string, unknown>, index: number) => void;
  /** Extract a stable key for a row. @default row.instance_id ?? index */
  rowKey?: (row: Record<string, unknown>, index: number) => string;
  /** Additional actions to render in the header next to search box */
  headerActions?: React.ReactNode;
  /** Custom actions to render at the end of each row. */
  rowActions?: (row: Record<string, unknown>) => React.ReactNode;
  /** Pass a new value to trigger a refresh. */
  refreshKey?: number;
}

/**
 * Generic Zino record-view table. Fetches `POST /app/{id}/view/recordview`
 * (server-side paginated + searchable) and renders config.fields as columns.
 * Wire it to a specific view via the thin wrappers in this folder.
 */
export function RecordView({
  client,
  rvUid,
  title = 'Records',
  columns,
  pageSize = 50,
  onRowClick,
  rowKey,
  headerActions,
  rowActions,
  refreshKey,
}: RecordViewProps) {
  const [page, setPage] = useState(1);
  const [search, setSearch] = useState('');
  const [debounced, setDebounced] = useState('');
  const [activeFilters, setActiveFilters] = useState<Record<string, string>>({});
  const [resp, setResp] = useState<RecordViewResponse | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  // Debounce the search box; reset to page 1 whenever the term changes.
  const timer = useRef<ReturnType<typeof setTimeout> | null>(null);
  useEffect(() => {
    if (timer.current) clearTimeout(timer.current);
    timer.current = setTimeout(() => {
      setDebounced(search);
      setPage(1);
    }, 300);
    return () => {
      if (timer.current) clearTimeout(timer.current);
    };
  }, [search]);

  const filtersParam = useMemo(() => {
    return Object.entries(activeFilters)
      .filter(([, val]) => val)
      .map(([key, val]) => ({
        field_key: key,
        value: val,
        data_type: 'string',
      }));
  }, [activeFilters]);

  useEffect(() => {
    let live = true;
    async function run() {
      setLoading(true);
      setError(null);
      try {
        const r = await client.recordView(rvUid, { page, limit: pageSize, search: debounced, filters: filtersParam });
        if (live) setResp(r);
      } catch (e) {
        if (live) setError((e as { message?: string })?.message ?? 'Failed to load');
      } finally {
        if (live) setLoading(false);
      }
    }
    run();
    return () => {
      live = false;
    };
  }, [client, rvUid, page, pageSize, debounced, filtersParam, refreshKey]);

  const fields: RecordViewField[] = useMemo(() => {
    const all = resp?.config.fields ?? [];
    if (!columns) return all;
    const byKey = new Map(all.map((f) => [f.field_key, f]));
    return columns
      .map((k) => byKey.get(k) ?? ({ field_key: k, output_label: k, data_type: 'string', is_filter: false, is_search: false } as RecordViewField))
      .filter(Boolean);
  }, [resp, columns]);

  const rows = resp?.data ?? [];
  const total = resp?.pagination?.total_count ?? rows.length;

  const tileValues = resp?.tile_values;

  const chartData = resp?.chart_data;

  const filterEntries = resp?.config?.filter_options
    ? Object.entries(resp.config.filter_options).filter(([, o]) => o && o.length > 0)
    : [];

  return (
    <div className="flex flex-col gap-5 w-full">
      <StatsTiles tiles={tileValues} />

      <AnalyticsChart data={chartData} />

      <div className="flex items-center justify-between gap-3">
        <h2 className="text-lg font-bold tracking-tight text-strong">{title}</h2>
        {headerActions}
      </div>

      <div className="flex flex-col gap-2.5">
        <div className="relative focus-ring rounded-pill border border-border-subtle bg-card shadow-sm">
          <Search size={16} className="absolute left-4 top-1/2 -translate-y-1/2 text-faint pointer-events-none" />
          <input
            value={search}
            onChange={(e) => setSearch(e.target.value)}
            placeholder="Search…"
            className="w-full h-11 rounded-pill bg-transparent pl-11 pr-4 font-sans text-sm text-strong outline-none placeholder:text-faint"
          />
        </div>
        {filterEntries.length > 0 && (
          <div className="flex gap-2 overflow-x-auto scrollbar-slim -mx-1 px-1 pb-1">
            {filterEntries.map(([key, options]) => {
              const fieldDef = resp?.config.fields.find(f => f.field_key === key);
              const label = fieldDef?.output_label || key;
              const opts = options!.map(o => ({ value: o, label: o }));
              return (
                <Select
                  key={key}
                  value={activeFilters[key] || ''}
                  onChange={(e) => {
                    setActiveFilters(prev => ({ ...prev, [key]: e.target.value }));
                    setPage(1);
                  }}
                  options={[{ value: '', label: `All ${label}` }, ...opts]}
                  className="w-40 shrink-0"
                />
              );
            })}
          </div>
        )}
      </div>

      {error ? (
        <EmptyState title="Couldn’t load records" hint={error} />
      ) : loading && !resp ? (
        <div className="p-10 flex justify-center">
          <Spinner label="Loading records…" />
        </div>
      ) : rows.length === 0 ? (
        <EmptyState title="No records" hint={debounced ? 'Nothing matches your search.' : undefined} />
      ) : (
        <div className="flex flex-col gap-3">
          {rows.map((row, i) => {
            const [primary, ...secondary] = fields;
            return (
              <div
                key={rowKey ? rowKey(row, i) : String(row.instance_id ?? i)}
                onClick={onRowClick ? () => onRowClick(row, i) : undefined}
                className={cn(
                  'rounded-xl border border-border-subtle bg-card p-4 shadow-sm transition-transform duration-150',
                  onRowClick && 'cursor-pointer active:scale-[0.99]',
                )}
              >
                <div className="flex items-center gap-3">
                  <div className="min-w-0 flex-1">
                    {primary && (
                      <p className="text-[15px] font-semibold text-strong truncate">
                        {formatValue(row[primary.field_key])}
                      </p>
                    )}
                    {secondary.length > 0 && (
                      <div className="mt-1 flex flex-col gap-0.5">
                        {secondary.map((f) => (
                          <p key={f.field_key} className="text-xs text-muted truncate">
                            <span className="text-faint">{f.output_label}:</span> {formatValue(row[f.field_key])}
                          </p>
                        ))}
                      </div>
                    )}
                  </div>
                  {onRowClick && (
                    <ChevronRight size={18} className="shrink-0 text-slate-300" />
                  )}
                </div>
                {rowActions && (
                  <div
                    onClick={(e) => e.stopPropagation()}
                    className="mt-3 pt-3 border-t border-border-subtle flex justify-end gap-2"
                  >
                    {rowActions(row)}
                  </div>
                )}
              </div>
            );
          })}
        </div>
      )}
      <Pagination page={page} pageSize={pageSize} total={total} onPage={setPage} />
    </div>
  );
}
