import { useMemo } from 'react';
import { Star, Radio } from 'lucide-react';
import { Badge } from '@/components/ui/badge';
import { cn } from '@/lib/utils';
import type { WorkedBeforeView } from '@/types';

type WorkedBefore = WorkedBeforeView;

interface Props {
  wb: WorkedBefore | null;
  busy: boolean;
  currentBand: string;
  currentMode: string;
  bands?: string[]; // operator's configured bands; falls back to DEFAULT_BANDS
  hasCall?: boolean; // a callsign is being entered — only then highlight the "current entry" cell
}

// Compact column label for a band tag: keep the classic V/U for 2m/70cm,
// strip the trailing "m" for meter bands (160m→160), and shorten cm bands
// (13cm→13c) so the column stays narrow.
function bandColLabel(tag: string): string {
  if (tag === '2m') return 'V';
  if (tag === '70cm') return 'U';
  if (tag.endsWith('cm')) return tag.replace('cm', 'c');
  return tag.replace(/m$/, '');
}

// Default 13-column band layout, used when the operator hasn't configured bands.
const DEFAULT_BANDS: { tag: string; label: string }[] = [
  { tag: '160m', label: '160' },
  { tag: '80m',  label: '80'  },
  { tag: '60m',  label: '60'  },
  { tag: '40m',  label: '40'  },
  { tag: '30m',  label: '30'  },
  { tag: '20m',  label: '20'  },
  { tag: '17m',  label: '17'  },
  { tag: '15m',  label: '15'  },
  { tag: '12m',  label: '12'  },
  { tag: '10m',  label: '10'  },
  { tag: '6m',   label: '6'   },
  { tag: '2m',   label: 'V'   },
  { tag: '70cm', label: 'U'   },
];
const CLASSES = ['PH', 'CW', 'DIG'] as const;

const PHONE_MODES = new Set(['SSB','USB','LSB','AM','FM','DIGITALVOICE','PHONE']);
function classMatchesMode(cls: string, mode: string): boolean {
  const u = (mode || '').toUpperCase();
  if (cls === 'PH') return PHONE_MODES.has(u);
  if (cls === 'CW') return u === 'CW';
  return u !== '' && u !== 'CW' && !PHONE_MODES.has(u);
}

// Dedicated matrix palette (--mx-* tokens, per theme in style.css): a 3-level
// ramp per hue so confirmed / worked / not-worked stay distinguishable on both
// light and dark surfaces (the generic status -muted fills were too dark on the
// dark themes to tell "worked" from "not worked" apart). Light-warm reproduces
// the original emerald/indigo/stone colours exactly.
const STATUS_CLASSES: Record<string, string> = {
  call_c: 'bg-mx-call-conf',
  call_w: 'bg-mx-call-work',
  dxcc_c: 'bg-mx-dx-conf',
  dxcc_w: 'bg-mx-dx-work',
};

// Legend entries, in the same colour order as the cells. swatch = the
// background class (or a special ring marker for the current-entry cell).
const LEGEND: { swatch: string; ring?: boolean; label: string }[] = [
  { swatch: 'bg-mx-call-conf', label: 'Call confirmed' },
  { swatch: 'bg-mx-call-work', label: 'Call worked' },
  { swatch: 'bg-mx-dx-conf', label: 'Entity confirmed' },
  { swatch: 'bg-mx-dx-work', label: 'Entity worked' },
  { swatch: 'bg-mx-none', label: 'Not worked' },
  { swatch: 'bg-mx-none', ring: true, label: 'Current entry' },
];

function cellTitle(band: string, cls: string, status: string, current: boolean): string {
  const desc =
    status === 'call_c' ? 'This callsign confirmed' :
    status === 'call_w' ? 'This callsign worked (not confirmed)' :
    status === 'dxcc_c' ? 'Entity confirmed (other callsign)' :
    status === 'dxcc_w' ? 'Entity worked (other callsign)' :
                          'Never worked';
  return `${band} ${cls}: ${desc}${current ? ' — current entry' : ''}`;
}

export function BandSlotGrid({ wb, busy, currentBand, currentMode, bands, hasCall = true }: Props) {
  // Columns from the operator's configured bands (so the matrix shows only the
  // bands they actually use), falling back to the built-in default set.
  const cols = useMemo(
    () => (bands && bands.length ? bands.map((tag) => ({ tag, label: bandColLabel(tag) })) : DEFAULT_BANDS),
    [bands],
  );
  const dxcc = wb?.dxcc ?? 0;
  const dxccName = wb?.dxcc_name ?? '';
  const dxccCount = wb?.dxcc_count ?? 0;
  const mwRank = wb?.mw_rank ?? 0; // ClubLog Most Wanted rank (1 = most wanted; 0 = off/unknown)
  const callCount = wb?.count ?? 0; // QSOs with this exact callsign
  const hasDxcc = dxcc > 0;
  const newOne = hasDxcc && dxccCount === 0;

  const statusMap = useMemo(() => {
    const m = new Map<string, string>();
    for (const s of wb?.band_status ?? []) {
      m.set(`${s.band}|${s.class}`, s.status);
    }
    return m;
  }, [wb]);

  // "Newness" of the current band+mode entry, for the award/DX-chase badges.
  // Derived straight from the entity's real band_status (all bands it was
  // worked on — not just the operator's configured column list).
  // By default newness uses the ACTUAL mode (FT8 / FT4 / RTTY…): DIG is a
  // group, so FT4 after FT8 is genuinely a new mode. The operator can opt into
  // DXCC-style grouping instead (Settings → General), where all digital modes
  // count as ONE — then FT4 after FT8 is just "worked".
  const groupDigital = localStorage.getItem('opslog.groupDigitalSlots') === '1';
  const normMode = (m: string): string => {
    const u = (m || '').toUpperCase().trim();
    if (!groupDigital) return u;
    return u === '' || u === 'CW' || PHONE_MODES.has(u) ? u : 'DIG';
  };
  const bandModes = (wb?.dxcc_band_modes ?? []) as { band: string; mode: string }[];
  const curMode = normMode(currentMode);
  const bandWorked = bandModes.some((bm) => bm.band === currentBand);          // entity worked on this band (any mode)
  const modeWorked = !!curMode && bandModes.some((bm) => normMode(bm.mode) === curMode); // …in this (normalised) mode (any band)
  const slotWorked = !!curMode && bandModes.some((bm) => bm.band === currentBand && normMode(bm.mode) === curMode);
  // Mutually-exclusive badges, shown only when the entity is worked but this
  // exact band+mode is NOT yet:
  //   New Band & Mode = both the band AND the mode are new for this entity.
  //   New Band        = the band is new (the mode was worked on another band).
  //   New Mode        = the mode is new (the band was worked in another mode).
  //   New Slot        = both band and mode already worked — just not together.
  const slotNew = hasDxcc && !newOne && !!curMode && !slotWorked;
  const newBandMode = slotNew && !bandWorked && !modeWorked;
  const newBand = slotNew && !bandWorked && modeWorked;
  const newMode = slotNew && bandWorked && !modeWorked;
  const newSlot = slotNew && bandWorked && modeWorked;

  // ClubLog Most Wanted rank pill (shown next to the entity name when the feature
  // is on). Hotter colour the more wanted the entity is.
  const mwBadge = mwRank > 0 ? (
    <Badge
      title={`ClubLog Most Wanted #${mwRank}`}
      className={cn('px-1.5 py-0 text-[10px] font-bold shrink-0',
        mwRank <= 100 ? 'bg-danger text-danger-foreground'
          : mwRank <= 250 ? 'bg-warning text-warning-foreground'
            : 'bg-muted text-muted-foreground')}
    >
      MW #{mwRank}
    </Badge>
  ) : null;

  return (
    <section
      className={cn(
        'flex items-center gap-4 px-3 py-2 flex-wrap shrink-0',
        newOne && 'bg-gradient-to-br from-warning-muted to-warning-muted rounded-lg',
      )}
    >
      <div className="flex items-center gap-2 min-w-[220px]">
        {newOne ? (
          <>
            <Badge className="bg-warning text-warning-foreground gap-1 px-3 py-1 text-[11px]">
              <Star className="size-3 fill-current" />
              NEW ONE
            </Badge>
            <span className="text-xs text-warning-muted-foreground">
              <strong className="text-warning-muted-foreground font-semibold">{dxccName || `DXCC #${dxcc}`}</strong>
              {' '}· never worked this entity
            </span>
            {mwBadge}
          </>
        ) : hasDxcc ? (
          <>
            <Badge className="bg-primary text-primary-foreground px-3 py-1 text-xs normal-case font-semibold tracking-normal">
              {dxccName || `DXCC #${dxcc}`}
            </Badge>
            {mwBadge}
            <span className="text-xs text-muted-foreground">
              <strong className="text-foreground font-semibold">{dxccCount}</strong>{' '}
              QSO{dxccCount > 1 ? 's' : ''} with this entity
              {callCount > 0 && (
                <>
                  {' · '}
                  <strong className="text-foreground font-semibold">{callCount}</strong>{' '}
                  with this call
                </>
              )}
            </span>
            {(newBand || newMode || newBandMode || newSlot) && (
              <div className="flex flex-wrap items-center gap-1">
                {newBandMode && <Badge className="bg-warning text-warning-foreground px-1.5 py-0 text-[10px]">New Band &amp; Mode</Badge>}
                {newBand && <Badge className="bg-warning text-warning-foreground px-1.5 py-0 text-[10px]">New Band</Badge>}
                {newMode && <Badge className="bg-warning text-warning-foreground px-1.5 py-0 text-[10px]">New Mode</Badge>}
                {newSlot && <Badge className="bg-info text-info-foreground px-1.5 py-0 text-[10px]">New Slot</Badge>}
              </div>
            )}
          </>
        ) : busy ? (
          <span className="flex items-center gap-2 text-xs text-muted-foreground italic">
            <Radio className="size-3.5 animate-pulse" />
            looking up…
          </span>
        ) : (
          <span className="text-xs text-muted-foreground italic">
            Type a callsign to see entity stats
          </span>
        )}
      </div>

      <div className="flex flex-col gap-2">
      <table className="border-separate" style={{ borderSpacing: 3 }}>
        <thead>
          <tr>
            <th className="w-[26px]" />
            {cols.map((b) => (
              <th
                key={b.tag}
                className={cn(
                  'font-mono text-[11px] font-semibold px-1 text-center',
                  b.tag === currentBand ? 'text-primary font-extrabold' : 'text-muted-foreground',
                )}
              >
                {b.label}
              </th>
            ))}
          </tr>
        </thead>
        <tbody>
          {CLASSES.map((cls) => {
            const classCurrent = classMatchesMode(cls, currentMode);
            return (
              <tr key={cls}>
                <th
                  className={cn(
                    'font-mono text-[11px] font-semibold pr-1.5 text-right w-[26px]',
                    classCurrent ? 'text-primary font-extrabold' : 'text-muted-foreground',
                  )}
                >
                  {cls}
                </th>
                {cols.map((b) => {
                  const st = statusMap.get(`${b.tag}|${cls}`) ?? '';
                  const isCurrent = hasCall && b.tag === currentBand && classCurrent;
                  return (
                    <td
                      key={b.tag}
                      title={cellTitle(b.tag, cls, st, isCurrent)}
                      className={cn(
                        'w-[28px] h-[24px] rounded transition-colors p-0',
                        st ? STATUS_CLASSES[st] : 'bg-mx-none',
                        isCurrent && 'ring-2 ring-warning ring-inset',
                      )}
                    />
                  );
                })}
              </tr>
            );
          })}
        </tbody>
      </table>

      {/* Colour legend — sits in the spare room under the matrix. */}
      <div className="flex items-center gap-x-3 gap-y-1 flex-wrap pl-[26px]">
        {LEGEND.map((l) => (
          <span key={l.label} className="flex items-center gap-1.5 text-[10px] text-muted-foreground">
            <span
              className={cn(
                'inline-block size-3 rounded shrink-0',
                l.swatch,
                l.ring && 'ring-2 ring-warning ring-inset',
              )}
            />
            {l.label}
          </span>
        ))}
      </div>
      </div>
    </section>
  );
}
