import { useMemo, useState } from 'react';
import { Loader2 } from 'lucide-react';
import { BulkUpdateField } from '../../wailsjs/go/main/App';
import {
  Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, DialogDescription,
} from '@/components/ui/dialog';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import {
  Select, SelectTrigger, SelectValue, SelectContent, SelectItem,
} from '@/components/ui/select';
import { useI18n } from '@/lib/i18n';

type FieldKind = 'status' | 'text' | 'freq' | 'date';
type FieldDef = { id: string; label: string; group: string; kind: FieldKind; upper?: boolean };

// Fields a bulk edit may target. status → Y/N/R/I dropdown; text → free input.
// upper:true uppercases code-like values (callsign, grid, refs). The id matches
// the backend BulkUpdateField whitelist.
// label holds an i18n key (resolved with t() at render time).
const FIELDS: FieldDef[] = [
  // QSL / upload status
  // One channel at a time, each with its status and its date, in the same order
  // as the QSL Info tab. "Upload" was the wrong word for half of them — a paper
  // QSL is not uploaded — so every entry now reads "<channel> sent/received
  // status" or "… date", which is also what the ADIF field is called.
  { id: 'qsl_sent',          label: 'bulk.fQslSent',         group: 'QSL / upload', kind: 'status' },
  { id: 'qsl_sent_date',     label: 'bulk.fQslSentDate',     group: 'QSL / upload', kind: 'date' },
  { id: 'qsl_rcvd',          label: 'bulk.fQslRcvd',         group: 'QSL / upload', kind: 'status' },
  { id: 'qsl_rcvd_date',     label: 'bulk.fQslRcvdDate',     group: 'QSL / upload', kind: 'date' },
  { id: 'qsl_via',           label: 'bulk.fQslVia',          group: 'QSL / upload', kind: 'text' },
  { id: 'qsl_sent_via',      label: 'bulk.fQslSentVia',      group: 'QSL / upload', kind: 'text' },
  { id: 'qsl_rcvd_via',      label: 'bulk.fQslRcvdVia',      group: 'QSL / upload', kind: 'text' },
  { id: 'lotw_sent',         label: 'bulk.fLotwSent',        group: 'QSL / upload', kind: 'status' },
  { id: 'lotw_sent_date',    label: 'bulk.fLotwSentDate',    group: 'QSL / upload', kind: 'date' },
  { id: 'lotw_rcvd',         label: 'bulk.fLotwRcvd',        group: 'QSL / upload', kind: 'status' },
  { id: 'lotw_rcvd_date',    label: 'bulk.fLotwRcvdDate',    group: 'QSL / upload', kind: 'date' },
  { id: 'eqsl_sent',         label: 'bulk.fEqslSent',        group: 'QSL / upload', kind: 'status' },
  { id: 'eqsl_sent_date',    label: 'bulk.fEqslSentDate',    group: 'QSL / upload', kind: 'date' },
  { id: 'eqsl_rcvd',         label: 'bulk.fEqslRcvd',        group: 'QSL / upload', kind: 'status' },
  { id: 'eqsl_rcvd_date',    label: 'bulk.fEqslRcvdDate',    group: 'QSL / upload', kind: 'date' },
  { id: 'qrz_sent',          label: 'bulk.fQrzSent',         group: 'QSL / upload', kind: 'status' },
  { id: 'qrz_sent_date',     label: 'bulk.fQrzSentDate',     group: 'QSL / upload', kind: 'date' },
  { id: 'qrz_rcvd',          label: 'bulk.fQrzRcvd',         group: 'QSL / upload', kind: 'status' },
  { id: 'qrz_rcvd_date',     label: 'bulk.fQrzRcvdDate',     group: 'QSL / upload', kind: 'date' },
  { id: 'clublog_sent',      label: 'bulk.fClublogSent',     group: 'QSL / upload', kind: 'status' },
  { id: 'clublog_sent_date', label: 'bulk.fClublogSentDate', group: 'QSL / upload', kind: 'date' },
  { id: 'hrdlog_sent',       label: 'bulk.fHrdlogSent',      group: 'QSL / upload', kind: 'status' },
  { id: 'hrdlog_sent_date',  label: 'bulk.fHrdlogSentDate',  group: 'QSL / upload', kind: 'date' },
  // HAMLOG.online: no promoted column, written into extras_json (see
  // qso.bulkEditableExtras). Status and date, like every other service here.
  { id: 'hamlog_sent',       label: 'bulk.fHamlogSent',      group: 'QSL / upload', kind: 'status' },
  { id: 'hamlog_sent_date',  label: 'bulk.fHamlogSentDate',  group: 'QSL / upload', kind: 'date' },
  { id: 'hamlog_rcvd',       label: 'bulk.fHamlogRcvd',      group: 'QSL / upload', kind: 'status' },
  { id: 'hamlog_rcvd_date',  label: 'bulk.fHamlogRcvdDate',  group: 'QSL / upload', kind: 'date' },
  // My station / operator
  { id: 'station_callsign', label: 'bulk.fStationCall',  group: 'My station', kind: 'text', upper: true },
  { id: 'operator',         label: 'bulk.fOperator',     group: 'My station', kind: 'text', upper: true },
  // No promoted column: written into extras_json (see qso.bulkEditableExtras).
  { id: 'owner_callsign',   label: 'bulk.fOwnerCallsign', group: 'My station', kind: 'text', upper: true },
  { id: 'my_grid',          label: 'bulk.fMyGrid',       group: 'My station', kind: 'text', upper: true },
  { id: 'my_antenna',       label: 'bulk.fMyAntenna',    group: 'My station', kind: 'text' },
  { id: 'my_rig',           label: 'bulk.fMyRig',        group: 'My station', kind: 'text' },
  { id: 'my_street',        label: 'bulk.fMyStreet',     group: 'My station', kind: 'text' },
  { id: 'my_city',          label: 'bulk.fMyCity',       group: 'My station', kind: 'text' },
  { id: 'my_postal_code',   label: 'bulk.fMyPostal',     group: 'My station', kind: 'text' },
  { id: 'my_country',       label: 'bulk.fMyCountry',    group: 'My station', kind: 'text' },
  { id: 'my_state',         label: 'bulk.fMyState',      group: 'My station', kind: 'text', upper: true },
  { id: 'my_cnty',          label: 'bulk.fMyCounty',     group: 'My station', kind: 'text' },
  { id: 'my_iota',          label: 'bulk.fMyIota',       group: 'My station', kind: 'text', upper: true },
  { id: 'my_sota_ref',      label: 'bulk.fMySota',       group: 'My station', kind: 'text', upper: true },
  { id: 'my_pota_ref',      label: 'bulk.fMyPota',       group: 'My station', kind: 'text', upper: true },
  { id: 'my_wwff_ref',      label: 'bulk.fMyWwff',       group: 'My station', kind: 'text', upper: true },
  { id: 'my_sig',           label: 'bulk.fMySig',        group: 'My station', kind: 'text' },
  { id: 'my_sig_info',      label: 'bulk.fMySigInfo',    group: 'My station', kind: 'text' },
  { id: 'my_name',          label: 'bulk.fMyName',       group: 'My station', kind: 'text' },
  { id: 'my_arrl_sect',     label: 'bulk.fMyArrlSect',   group: 'My station', kind: 'text', upper: true },
  { id: 'my_darc_dok',      label: 'bulk.fMyDarcDok',    group: 'My station', kind: 'text', upper: true },
  { id: 'my_vucc_grids',    label: 'bulk.fMyVuccGrids',  group: 'My station', kind: 'text', upper: true },
  // Contest
  { id: 'contest_id', label: 'bulk.fContestId',  group: 'Contest', kind: 'text', upper: true },
  { id: 'srx_string', label: 'bulk.fSrxString',  group: 'Contest', kind: 'text' },
  { id: 'stx_string', label: 'bulk.fStxString',  group: 'Contest', kind: 'text' },
  { id: 'arrl_sect',  label: 'bulk.fArrlSect',   group: 'Contest', kind: 'text', upper: true },
  { id: 'precedence', label: 'bulk.fPrecedence', group: 'Contest', kind: 'text', upper: true },
  { id: 'class',      label: 'bulk.fClass',      group: 'Contest', kind: 'text', upper: true },
  // Propagation / satellite
  { id: 'prop_mode', label: 'bulk.fPropMode', group: 'Propagation', kind: 'text', upper: true },
  { id: 'sat_name',  label: 'bulk.fSatName',  group: 'Propagation', kind: 'text', upper: true },
  { id: 'sat_mode',  label: 'bulk.fSatMode',  group: 'Propagation', kind: 'text', upper: true },
  // Contacted station (location / activation refs / SIG)
  { id: 'grid',     label: 'bulk.fGrid',    group: 'Contacted station', kind: 'text', upper: true },
  { id: 'state',    label: 'bulk.fState',   group: 'Contacted station', kind: 'text', upper: true },
  { id: 'cnty',     label: 'bulk.fCnty',    group: 'Contacted station', kind: 'text' },
  { id: 'pota_ref', label: 'bulk.fPotaRef', group: 'Contacted station', kind: 'text', upper: true },
  { id: 'sota_ref', label: 'bulk.fSotaRef', group: 'Contacted station', kind: 'text', upper: true },
  { id: 'wwff_ref', label: 'bulk.fWwffRef', group: 'Contacted station', kind: 'text', upper: true },
  { id: 'iota',     label: 'bulk.fIota',    group: 'Contacted station', kind: 'text', upper: true },
  { id: 'sig',      label: 'bulk.fSig',     group: 'Contacted station', kind: 'text' },
  { id: 'sig_info', label: 'bulk.fSigInfo', group: 'Contacted station', kind: 'text' },
  // The contact itself — repair fields, not description fields. An import that
  // mapped every QSO to SSB, or an ADIF with no MODE at all, is fixed here
  // instead of one row at a time.
  //
  // Band is deliberately absent: it travels with the frequency below, because a
  // band contradicting its own frequency is invalid ADIF and every export would
  // carry the contradiction.
  { id: 'mode',     label: 'bulk.fMode',    group: 'The contact', kind: 'text', upper: true },
  { id: 'submode',  label: 'bulk.fSubmode', group: 'The contact', kind: 'text', upper: true },
  { id: 'rst_sent', label: 'bulk.fRstSent', group: 'The contact', kind: 'text' },
  { id: 'rst_rcvd', label: 'bulk.fRstRcvd', group: 'The contact', kind: 'text' },
  // Misc
  // Frequency (MHz) — sets freq_hz AND recomputes band. Main use: fixing a batch
  // logged on a stale/default frequency after CAT dropped.
  { id: 'freq',    label: 'bulk.fFreq',    group: 'Misc', kind: 'freq' },
  { id: 'comment', label: 'bulk.fComment', group: 'Misc', kind: 'text' },
  { id: 'notes',   label: 'bulk.fNotes',   group: 'Misc', kind: 'text' },
  { id: 'rig',     label: 'bulk.fRig',     group: 'Misc', kind: 'text' },
  { id: 'ant',     label: 'bulk.fAnt',     group: 'Misc', kind: 'text' },
];

// label holds an i18n key.
const STATUS_VALUES: { v: string; label: string }[] = [
  { v: 'Y', label: 'bulk.statusY' },
  { v: 'N', label: 'bulk.statusN' },
  { v: 'R', label: 'bulk.statusR' },
  { v: 'I', label: 'bulk.statusI' },
  { v: '_', label: 'bulk.statusBlank' },
];

// Derived from the fields themselves, in the order they are declared.
//
// This used to be a hand-written list, and a group added to FIELDS but not to it
// simply never rendered — the fields existed, passed every check, and could not
// be picked. Two lists that must agree, with nothing to make them.
const GROUPS = [...new Set(FIELDS.map((f) => f.group))];
// Maps the internal group key → its i18n label key.
const GROUP_LABELS: Record<string, string> = {
  'QSL / upload': 'bulk.groupQsl',
  'My station': 'bulk.groupMyStation',
  'Contacted station': 'bulk.groupContacted',
  'Contest': 'bulk.groupContest',
  'Propagation': 'bulk.groupPropagation',
  'Misc': 'bulk.groupMisc',
  'The contact': 'bulk.groupContact',
};

type Props = {
  open: boolean;
  ids: number[];
  onClose: () => void;
  onApplied: (n: number) => void;
};

// BulkEditModal sets one QSL/upload status field to a chosen value across the
// selected QSOs — e.g. flip a filtered batch of imported contacts from N to R
// so they become eligible for upload.
export function BulkEditModal({ open, ids, onClose, onApplied }: Props) {
  const { t } = useI18n();
  const [field, setField] = useState('qsl_sent');
  const [statusValue, setStatusValue] = useState('R');
  const [textValue, setTextValue] = useState('');
  const [busy, setBusy] = useState(false);
  const [error, setError] = useState('');

  const def = useMemo(() => FIELDS.find((f) => f.id === field) ?? FIELDS[0], [field]);
  const isStatus = def.kind === 'status';
  const effectiveValue = isStatus
    ? (statusValue === '_' ? '' : statusValue)
    : textValue.trim();

  async function apply() {
    setBusy(true);
    setError('');
    try {
      const n = await BulkUpdateField(ids as any, field, effectiveValue);
      onApplied(Number(n) || 0);
      onClose();
    } catch (e: any) {
      setError(String(e?.message ?? e));
    } finally {
      setBusy(false);
    }
  }

  return (
    <Dialog open={open} onOpenChange={(o) => { if (!o) onClose(); }}>
      <DialogContent className="max-w-md">
        <DialogHeader>
          <DialogTitle>{t('bulk.title')}</DialogTitle>
          <DialogDescription>
            {t('bulk.desc', { n: ids.length })}
          </DialogDescription>
        </DialogHeader>

        <div className="px-5 py-2 space-y-3">
          <div className="grid grid-cols-[90px_1fr] gap-3 items-center">
            <Label className="text-sm">{t('bulk.fieldLabel')}</Label>
            <Select value={field} onValueChange={setField}>
              <SelectTrigger className="h-8"><SelectValue /></SelectTrigger>
              <SelectContent>
                {GROUPS.map((g) => (
                  <div key={g}>
                    <div className="px-2 py-1 text-[10px] uppercase tracking-wider text-muted-foreground">{GROUP_LABELS[g] ? t(GROUP_LABELS[g]) : g}</div>
                    {FIELDS.filter((f) => f.group === g)
                      .map((f) => ({ f, txt: t(f.label) }))
                      .sort((a, b) => a.txt.localeCompare(b.txt))
                      .map(({ f, txt }) => (
                        <SelectItem key={f.id} value={f.id}>{txt}</SelectItem>
                      ))}
                  </div>
                ))}
              </SelectContent>
            </Select>

            <Label className="text-sm">{t('bulk.valueLabel')}</Label>
            {isStatus ? (
              <Select value={statusValue} onValueChange={setStatusValue}>
                <SelectTrigger className="h-8"><SelectValue /></SelectTrigger>
                <SelectContent>
                  {STATUS_VALUES.map((v) => <SelectItem key={v.v} value={v.v}>{t(v.label)}</SelectItem>)}
                </SelectContent>
              </Select>
            ) : def.kind === 'date' ? (
              // ADIF stores YYYYMMDD; the picker speaks YYYY-MM-DD. Clearing it
              // writes an empty value — which is how a wrong date is removed
              // from a batch, the same meaning as leaving a text field blank.
              <div className="flex gap-1">
                <Input
                  type="date"
                  className="h-8 text-xs font-mono"
                  value={/^\d{8}$/.test(textValue) ? `${textValue.slice(0, 4)}-${textValue.slice(4, 6)}-${textValue.slice(6, 8)}` : ''}
                  onChange={(e) => setTextValue(e.target.value ? e.target.value.replace(/-/g, '') : '')}
                />
                <Button
                  type="button" variant="outline" size="sm" className="h-8 shrink-0 px-2"
                  title={t('bulk.todayUtc')}
                  onClick={() => {
                    const d = new Date();
                    const pad = (n: number) => String(n).padStart(2, '0');
                    // UTC: the whole log is UTC, and near midnight the local day
                    // is the wrong one.
                    setTextValue(`${d.getUTCFullYear()}${pad(d.getUTCMonth() + 1)}${pad(d.getUTCDate())}`);
                  }}
                >{t('bulk.today')}</Button>
              </div>
            ) : (
              <Input
                className="h-8 text-xs"
                value={textValue}
                inputMode={def.kind === 'freq' ? 'decimal' : undefined}
                placeholder={def.kind === 'freq' ? t('bulk.freqPlaceholder') : t('bulk.clearPlaceholder')}
                onChange={(e) => setTextValue(def.upper ? e.target.value.toUpperCase() : e.target.value)}
              />
            )}
          </div>

          <div className="text-[11px] text-muted-foreground">
            {t('bulk.willSet')} <span className="font-semibold">{t(def.label)}</span> ={' '}
            <span className="font-mono">{effectiveValue === '' ? t('bulk.blank') : effectiveValue}{def.kind === 'freq' && effectiveValue !== '' ? ' MHz' : ''}</span> {t('bulk.onQsos', { n: ids.length })}
          </div>
          {error && <div className="text-xs text-danger">{error}</div>}
        </div>

        <DialogFooter>
          <Button variant="outline" onClick={onClose} disabled={busy}>{t('bulk.cancel')}</Button>
          <Button onClick={apply} disabled={busy || ids.length === 0}>
            {busy ? <Loader2 className="size-3.5 animate-spin" /> : null}
            {t('bulk.applyTo', { n: ids.length })}
          </Button>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}
