// src/components/ui/CommonTimelineModal.tsx
'use client';

import { useEffect, useState, useCallback, useSyncExternalStore, startTransition } from 'react';
import { createPortal } from 'react-dom';
import { X, Clock, User, History } from 'lucide-react';

// ─── Types ────────────────────────────────────────────────────────────────────

interface DiffEntry {
  path: string[];
  type: 'changed' | 'added' | 'removed';
  before?: unknown;
  after?: unknown;
}

interface TimelineEntry {
  id: string;
  version: number;
  action: string;
  actorEmail: string | null;
  diff: DiffEntry[];
  createdAt: string;
}

interface CommonTimelineModalProps {
  isOpen: boolean;
  onClose: () => void;
  entityType: string;
  entityId: string;
  entityName?: string;
  title?: string;
}

// ─── Mounted check (SSR-safe, no effect + setState needed) ──────────────────

function subscribeNoop() {
  return () => {};
}
function getClientSnapshot() {
  return true;
}
function getServerSnapshot() {
  return false;
}

function useMounted(): boolean {
  return useSyncExternalStore(subscribeNoop, getClientSnapshot, getServerSnapshot);
}

// ─── Action badge colors ────────────────────────────────────────────────────

const SUCCESS = { bg: 'var(--color-success-light)', color: 'var(--color-success)' };
const INFO    = { bg: 'var(--color-info-light)',    color: 'var(--color-info)' };
const DANGER  = { bg: 'var(--color-danger-light)',  color: 'var(--color-danger)' };
const NEUTRAL = { bg: 'var(--color-surface-alt)',   color: 'var(--color-text-tertiary)' };

// Every module's timeline (audit_logs entities, order status history, payment
// history) goes through this modal, so the badge colour is derived from the
// action's meaning rather than an exact-match list that greys out anything new.
function actionStyle(action: string): { bg: string; color: string } {
  const a = action.toUpperCase();
  if (a.includes('DELETE') || a.includes('REJECT') || a.includes('CANCEL') || a.includes('FAIL')) return DANGER;
  if (a.includes('CREATE') || a.includes('RESTORE') || a.includes('ADD') || a.includes('APPROVE')) return SUCCESS;
  if (a.includes('UPDATE') || a.includes('CHANGE') || a.includes('EDIT') || a.includes('STATUS')) return INFO;
  return NEUTRAL;
}

function ActionBadge({ action }: { action: string }) {
  const style = actionStyle(action);
  return (
    <span
      className="inline-flex px-2 py-0.5 text-[11px] font-semibold rounded-full uppercase tracking-wide"
      style={{ background: style.bg, color: style.color }}
    >
      {action.replace(/_/g, ' ')}
    </span>
  );
}

// ─── Diff row rendering ─────────────────────────────────────────────────────

const DIFF_TYPE_STYLES: Record<DiffEntry['type'], { bg: string; color: string; label: string }> = {
  changed: { bg: 'var(--color-info-light)',    color: 'var(--color-info)',    label: 'Changed' },
  added:   { bg: 'var(--color-success-light)', color: 'var(--color-success)', label: 'Added' },
  removed: { bg: 'var(--color-danger-light)',  color: 'var(--color-danger)',  label: 'Removed' },
};

function formatFieldPath(path: string[]): string {
  return path
    .map((segment) =>
      /^[a-z0-9_]+$/i.test(segment) && segment.includes('_')
        ? segment.split('_').map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(' ')
        : segment,
    )
    .join(' → ');
}

function isBooleanLike(value: unknown): boolean {
  return (
    typeof value === 'boolean' ||
    value === 0 || value === 1 ||
    value === '0' || value === '1'
  );
}

function toYesNo(value: unknown): string {
  return value === true || value === 1 || value === '1' ? 'Yes' : 'No';
}

// ISO date strings (jo audit.service.ts ne Date -> ISO ki hain) detect karne ke liye
const ISO_DATE_REGEX = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?Z$/;

function isIsoDateString(value: unknown): value is string {
  return typeof value === 'string' && ISO_DATE_REGEX.test(value);
}

/**
 * Value ko display ke liye format karta hai. Agar value ISO date string hai,
 * to diye gaye timezone mein human-readable date/time banata hai — yeh
 * SIRF display ke liye hai, DB mein hamesha UTC hi rehti hai.
 */
function formatDiffValue(value: unknown, timezone: string): string {
  if (value === null || value === undefined || value === '') return 'empty';
  if (typeof value === 'boolean') return value ? 'Yes' : 'No';

  if (isIsoDateString(value)) {
    try {
      return new Intl.DateTimeFormat('en-GB', {
        timeZone: timezone,
        year: 'numeric',
        month: 'short',
        day: '2-digit',
        hour: '2-digit',
        minute: '2-digit',
        hour12: true,
      }).format(new Date(value));
    } catch {
      return value; // agar timezone invalid ho to raw value dikha dein
    }
  }

  if (typeof value === 'object') {
    try { return JSON.stringify(value); } catch { return String(value); }
  }
  return String(value);
}

// before/after ko SAATH mein dekh kar format karta hai — taake "0 → true" jaisi
// mismatched-but-same-meaning values dono consistently "No"/"Yes" ban jayein.
function formatDiffPair(before: unknown, after: unknown, timezone: string): { beforeText: string; afterText: string } {
  if (isBooleanLike(before) && isBooleanLike(after)) {
    return { beforeText: toYesNo(before), afterText: toYesNo(after) };
  }
  return {
    beforeText: formatDiffValue(before, timezone),
    afterText: formatDiffValue(after, timezone),
  };
}

function formatTimestamp(iso: string, timezone: string): string {
  try {
    return new Intl.DateTimeFormat('en-GB', {
      timeZone: timezone,
      year: 'numeric',
      month: 'short',
      day: '2-digit',
      hour: '2-digit',
      minute: '2-digit',
      hour12: true,
    }).format(new Date(iso));
  } catch {
    return new Date(iso).toLocaleString();
  }
}

function DiffRow({ entry, timezone }: { entry: DiffEntry; timezone: string }) {
  const style = DIFF_TYPE_STYLES[entry.type];

  const pairFormatted = entry.type === 'changed'
    ? formatDiffPair(entry.before, entry.after, timezone)
    : null;

  return (
    <div className="flex items-start gap-3 py-2 border-b last:border-b-0" style={{ borderColor: 'var(--color-border)' }}>
      <span
        className="inline-flex px-2 py-0.5 text-[10px] font-semibold rounded-full uppercase shrink-0 mt-0.5"
        style={{ background: style.bg, color: style.color }}
      >
        {style.label}
      </span>
      <div className="flex-1 min-w-0">
        <div className="text-xs font-medium mb-0.5 wrap-break-word" style={{ color: 'var(--color-text)' }}>
          {formatFieldPath(entry.path)}
        </div>
        <div className="text-xs wrap-break-word" style={{ color: 'var(--color-text-secondary)' }}>
          {entry.type === 'changed' && pairFormatted && (
            <>
              <span style={{ color: 'var(--color-danger)' }}>{pairFormatted.beforeText}</span>
              {' → '}
              <span style={{ color: 'var(--color-success)' }}>{pairFormatted.afterText}</span>
            </>
          )}
          {entry.type === 'added' && <span>{formatDiffValue(entry.after, timezone)}</span>}
          {entry.type === 'removed' && <span>{formatDiffValue(entry.before, timezone)}</span>}
        </div>
      </div>
    </div>
  );
}

// ─── Skeleton ─────────────────────────────────────────────────────────────────

function TimelineSkeleton() {
  return (
    <div className="space-y-5">
      {[1, 2, 3].map((i) => (
        <div key={i} className="flex gap-3 animate-pulse">
          <div className="w-2.5 h-2.5 rounded-full mt-1.5 shrink-0" style={{ background: 'var(--color-border)' }} />
          <div className="flex-1 space-y-2">
            <div className="h-3 w-32 rounded" style={{ background: 'var(--color-border)' }} />
            <div className="h-3 w-3/4 rounded" style={{ background: 'var(--color-border)' }} />
          </div>
        </div>
      ))}
    </div>
  );
}

// ─── Main Component ───────────────────────────────────────────────────────────

export default function CommonTimelineModal({
  isOpen,
  onClose,
  entityType,
  entityId,
  entityName,
  title,
}: CommonTimelineModalProps) {
  const [timeline, setTimeline] = useState<TimelineEntry[]>([]);
  const [timezone, setTimezone] = useState('UTC');
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);

  const mounted = useMounted();

  // ── Fetch timeline ──────────────────────────────────────────────────────
  useEffect(() => {
    if (!isOpen || !entityId) return;

    const ctrl = new AbortController();

    startTransition(() => {
      setLoading(true);
      setError(null);
    });

    fetch(`/api/timeline/${entityType}/${entityId}`, { signal: ctrl.signal, cache: 'no-store' })
      .then((res) => {
        if (!res.ok) throw new Error('Failed to fetch timeline');
        return res.json();
      })
      .then((json) => {
        startTransition(() => {
          setTimeline(json?.data?.timeline ?? []);
          setTimezone(json?.data?.timezone ?? 'UTC');
        });
      })
      .catch((err) => {
        if (err.name === 'AbortError') return;
        console.error('[CommonTimelineModal]', err);
        startTransition(() => {
          setError('Failed to load timeline');
        });
      })
      .finally(() => {
        startTransition(() => {
          setLoading(false);
        });
      });

    return () => ctrl.abort();
  }, [isOpen, entityType, entityId]);

  // ── Lock body scroll + Esc to close ─────────────────────────────────────
  useEffect(() => {
    if (!isOpen) return;

    const originalOverflow = document.body.style.overflow;
    document.body.style.overflow = 'hidden';

    const handleKeyDown = (e: KeyboardEvent) => {
      if (e.key === 'Escape') onClose();
    };
    window.addEventListener('keydown', handleKeyDown);

    return () => {
      document.body.style.overflow = originalOverflow;
      window.removeEventListener('keydown', handleKeyDown);
    };
  }, [isOpen, onClose]);

  const handleOverlayClick = useCallback((e: React.MouseEvent) => {
    if (e.target === e.currentTarget) onClose();
  }, [onClose]);

  if (!mounted || !isOpen) return null;

  const modalContent = (
    <div
      className="fixed inset-0 z-100 flex items-center justify-center p-4"
      style={{ background: 'rgba(0, 0, 0, 0.5)' }}
      onClick={handleOverlayClick}
    >
      <div
        className="w-full max-w-xl rounded-xl shadow-xl flex flex-col max-h-[80vh]"
        style={{ background: 'var(--color-surface)', border: '1px solid var(--color-border)' }}
      >
        {/* Header */}
        <div
          className="flex items-center justify-between px-5 py-4 border-b shrink-0"
          style={{ borderColor: 'var(--color-border)' }}
        >
          <div className="flex items-center gap-2">
            <History size={18} style={{ color: 'var(--color-text-secondary)' }} />
            <h3 className="text-base font-semibold" style={{ color: 'var(--color-text)' }}>
              {title || `${entityName ?? 'Record'} Timeline`}
            </h3>
          </div>
          <button
            onClick={onClose}
            className="p-1.5 rounded-lg hover:bg-surface-alt transition-colors"
            style={{ color: 'var(--color-text-tertiary)' }}
            aria-label="Close"
          >
            <X size={18} />
          </button>
        </div>

        {/* Body */}
        <div className="px-5 py-4 overflow-y-auto flex-1">
          {loading && <TimelineSkeleton />}

          {!loading && error && (
            <p className="text-sm" style={{ color: 'var(--color-danger)' }}>{error}</p>
          )}

          {!loading && !error && timeline.length === 0 && (
            <p className="text-sm" style={{ color: 'var(--color-text-tertiary)' }}>
              No history found for this record.
            </p>
          )}

          {!loading && !error && timeline.length > 0 && (
            <div className="relative">
              {/* Vertical line */}
              <div
                className="absolute left-1.25 top-2 bottom-2 w-px"
                style={{ background: 'var(--color-border)' }}
              />
              <div className="space-y-5">
                {timeline.map((entry) => (
                  <div key={entry.id} className="relative pl-6">
                    {/* Dot */}
                    <div
                      className="absolute left-0 top-1.5 w-2.75 h-2.75 rounded-full border-2"
                      style={{ background: 'var(--color-surface)', borderColor: 'var(--color-cta)' }}
                    />
                    <div className="flex flex-wrap items-center gap-2 mb-2">
                      <span className="text-xs font-semibold" style={{ color: 'var(--color-text)' }}>
                        v{entry.version}
                      </span>
                      <ActionBadge action={entry.action} />
                      <span
                        className="inline-flex items-center gap-1 text-[11px]"
                        style={{ color: 'var(--color-text-tertiary)' }}
                      >
                        <Clock size={11} />
                        {formatTimestamp(entry.createdAt, timezone)}
                      </span>
                      {entry.actorEmail && (
                        <span
                          className="inline-flex items-center gap-1 text-[11px]"
                          style={{ color: 'var(--color-text-tertiary)' }}
                        >
                          <User size={11} />
                          {entry.actorEmail}
                        </span>
                      )}
                    </div>

                    {/* Diff list */}
                    <div className="rounded-lg border" style={{ borderColor: 'var(--color-border)' }}>
                      {entry.diff.length === 0 ? (
                        <p className="text-xs px-3 py-2" style={{ color: 'var(--color-text-tertiary)' }}>
                          No field-level changes recorded
                        </p>
                      ) : (
                        <div className="px-3">
                          {entry.diff.map((d, i) => (
                            <DiffRow key={i} entry={d} timezone={timezone} />
                          ))}
                        </div>
                      )}
                    </div>
                  </div>
                ))}
              </div>
            </div>
          )}
        </div>
      </div>
    </div>
  );

  return createPortal(modalContent, document.body);
}