import Head from 'next/head';
import Link from 'next/link';
import { FormEvent, useEffect, useRef, useState } from 'react';
import { api } from '../../lib/api';
import { useRequireAuth } from '../../lib/auth-context';

interface JobOption {
  id: string;
  ticketNumber: string;
  title: string;
  status: string;
}

interface SentEntry {
  at: string;
  seq: number;
  type: MessageType;
  lat: number;
  lng: number;
  depth: number;
  transport: Transport;
}

type MessageType = 'log' | 'status';
type Transport = 'relay' | 'mqtts';

const UTILITIES = ['GAS', 'WATER', 'ELECTRIC', 'SEWER', 'TELECOM', 'CATV', 'FIBER', 'STEAM', 'UNKNOWN'];
const FIX_TYPES = ['FIXED_RTK', 'FLOAT_RTK', 'DGPS', 'AUTONOMOUS', 'NONE'];
const LOCATE_MODES = ['PEAK', 'NULL', 'BROAD_PEAK', 'SONDE'];

const SERIAL_KEY = 'ulhub_sim_serial';

function jitter(base: number, spread: number): number {
  return Math.round((base + (Math.random() * 2 - 1) * spread) * 100) / 100;
}

// Advances a lat/lng by `distanceM` meters along `headingDeg` (0 = north, 90 = east)
function step(lat: number, lng: number, headingDeg: number, distanceM: number) {
  const rad = (headingDeg * Math.PI) / 180;
  const dLat = (distanceM * Math.cos(rad)) / 111320;
  const dLng = (distanceM * Math.sin(rad)) / (111320 * Math.cos((lat * Math.PI) / 180));
  return { lat: lat + dLat, lng: lng + dLng };
}

export default function SimulatorPage() {
  const { user, activeOrg, loading } = useRequireAuth();
  const orgId = activeOrg?.org.id ?? null;

  const [jobs, setJobs] = useState<JobOption[]>([]);
  const [jobId, setJobId] = useState('');
  const [creatingJob, setCreatingJob] = useState(false);
  const [newTicket, setNewTicket] = useState('');
  const [newTitle, setNewTitle] = useState('');

  const [messageType, setMessageType] = useState<MessageType>('log');
  const [transport, setTransport] = useState<Transport>('relay');
  const [serial, setSerial] = useState('');
  const [utility, setUtility] = useState('GAS');
  const [fixType, setFixType] = useState('FIXED_RTK');
  const [locateMode, setLocateMode] = useState('PEAK');
  const [depth, setDepth] = useState(1.2);
  const [frequencyHz, setFrequencyHz] = useState(33000);
  const [currentMa, setCurrentMa] = useState(45);
  const [originLat, setOriginLat] = useState(33.15012345);
  const [originLng, setOriginLng] = useState(-96.83512345);
  const [headingDeg, setHeadingDeg] = useState(45);
  const [stepMeters, setStepMeters] = useState(1.5);
  const [intervalSec, setIntervalSec] = useState(2);

  const [count, setCount] = useState(0);
  const [autoSending, setAutoSending] = useState(false);
  const [sent, setSent] = useState<SentEntry[]>([]);
  const [error, setError] = useState<string | null>(null);
  const timerRef = useRef<ReturnType<typeof setInterval> | null>(null);

  useEffect(() => {
    const stored = localStorage.getItem(SERIAL_KEY);
    setSerial(stored || `SIM-${Math.floor(1000 + Math.random() * 9000)}`);
  }, []);

  useEffect(() => {
    if (!orgId) {
      return;
    }
    api
      .get<{ jobs: JobOption[] }>(`/api/orgs/${orgId}/jobs?limit=100`)
      .then((res) => {
        const openish = res.jobs.filter((j) => j.status === 'OPEN' || j.status === 'IN_PROGRESS');
        setJobs(openish);
        if (!jobId && openish[0]) {
          setJobId(openish[0].id);
        }
      })
      .catch((err) => setError(err.message));
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [orgId]);

  useEffect(() => {
    return () => {
      if (timerRef.current) {
        clearInterval(timerRef.current);
      }
    };
  }, []);

  // setInterval captures whatever closure exists when it's created; routing
  // every tick through a ref that's refreshed each render keeps it reading
  // current state (count, serial, path config, …) instead of stale values.
  const sendPointRef = useRef<() => void>(() => {});

  const createJob = async (e: FormEvent) => {
    e.preventDefault();
    if (!orgId) {
      return;
    }
    try {
      const job = await api.post<JobOption>(`/api/orgs/${orgId}/jobs`, {
        ticketNumber: newTicket,
        title: newTitle,
      });
      setJobs((j) => [{ ...job }, ...j]);
      setJobId(job.id);
      setCreatingJob(false);
      setNewTicket('');
      setNewTitle('');
    } catch (err: any) {
      setError(err.message);
    }
  };

  const sendPoint = async () => {
    if (!orgId || !jobId || !serial) {
      return;
    }
    localStorage.setItem(SERIAL_KEY, serial);
    const pos = step(originLat, originLng, headingDeg, stepMeters * count);
    const seq = count + 1;
    const pointDepth = Math.max(0.1, jitter(depth, 0.15));
    try {
      await api.post(`/api/orgs/${orgId}/sim/publish`, {
        type: messageType,
        serial,
        transport,
        jobId,
        lat: pos.lat,
        lng: pos.lng,
        fix: fixType,
        hAcc: 0.014,
        vAcc: 0.02,
        sats: Math.round(jitter(22, 3)),
        hdop: 0.7,
        depth: pointDepth,
        freqHz: frequencyHz,
        currentMa: Math.max(0, jitter(currentMa, 3)),
        signalDb: jitter(60, 5),
        gainDb: 40,
        mode: locateMode,
        compassDeg: jitter(headingDeg, 4),
        distortionPct: Math.max(0, jitter(4, 2)),
        utility,
        seq,
        ts: new Date().toISOString(),
      });
      setCount(seq);
      setSent((prev) =>
        [
          {
            at: new Date().toLocaleTimeString(),
            seq,
            type: messageType,
            lat: pos.lat,
            lng: pos.lng,
            depth: pointDepth,
            transport,
          },
          ...prev,
        ].slice(0, 25),
      );
      setError(null);
    } catch (err: any) {
      setError(err.message);
      setAutoSending(false);
    }
  };

  sendPointRef.current = sendPoint;

  const toggleAutoSend = () => {
    if (autoSending) {
      if (timerRef.current) {
        clearInterval(timerRef.current);
      }
      setAutoSending(false);
      return;
    }
    setAutoSending(true);
    sendPointRef.current();
    timerRef.current = setInterval(() => sendPointRef.current(), intervalSec * 1000);
  };

  const reset = () => {
    if (timerRef.current) {
      clearInterval(timerRef.current);
    }
    setAutoSending(false);
    setCount(0);
    setSent([]);
  };

  if (loading || !user) {
    return null;
  }

  const selectedJob = jobs.find((j) => j.id === jobId);

  return (
    <div style={{ fontFamily: 'sans-serif', minHeight: '100vh', background: '#fafafa' }}>
      <Head>
        <title>Transmitter Simulator · UlHub</title>
      </Head>
      <header style={{ padding: '0.75rem 1.5rem', borderBottom: '1px solid #ddd', background: '#212121', color: '#fff' }}>
        <strong>UlHub</strong> <span style={{ opacity: 0.7 }}>· Transmitter Simulator</span>
        <Link href="/" style={{ float: 'right', color: '#9cc9ff' }}>
          ← Back to app
        </Link>
      </header>

      <main style={{ maxWidth: 720, margin: '0 auto', padding: '1.5rem' }}>
        <p style={{ color: '#666' }}>
          Simulates a locator receiver publishing GPS + telemetry to the MQTT broker at{' '}
          <code>devices/{serial || '<serial>'}/log</code>.
        </p>
        {error && <p style={{ color: '#c62828' }}>{error}</p>}

        <fieldset style={{ marginBottom: '1rem', border: '1px solid #ddd', borderRadius: 8, padding: '1rem' }}>
          <legend>Message type</legend>
          <label style={{ marginRight: '1.5rem' }}>
            <input type="radio" checked={messageType === 'log'} onChange={() => setMessageType('log')} /> Log point{' '}
            <span style={{ color: '#888' }}>— persisted, appears on the utility line</span>
          </label>
          <label>
            <input type="radio" checked={messageType === 'status'} onChange={() => setMessageType('status')} /> Status
            update <span style={{ color: '#888' }}>— live position only, not saved</span>
          </label>
        </fieldset>

        <fieldset style={{ marginBottom: '1rem', border: '1px solid #ddd', borderRadius: 8, padding: '1rem' }}>
          <legend>Connection</legend>
          <label style={{ marginRight: '1.5rem' }}>
            <input type="radio" checked={transport === 'relay'} onChange={() => setTransport('relay')} /> HTTP relay{' '}
            <span style={{ color: '#888' }}>— backend forwards it on the broker connection it already holds</span>
          </label>
          <label>
            <input type="radio" checked={transport === 'mqtts'} onChange={() => setTransport('mqtts')} /> MQTTS (device
            certificate) <span style={{ color: '#888' }}>— connects to port 8883 and authenticates as this serial's own client cert</span>
          </label>
          {transport === 'mqtts' && (
            <p style={{ color: '#999', fontSize: '0.85rem', margin: '0.5rem 0 0' }}>
              Requires a device with this exact serial number to already exist in this org and have a certificate
              issued from <a href="/settings/devices">Devices</a>.
            </p>
          )}
        </fieldset>

        <fieldset style={{ marginBottom: '1rem', border: '1px solid #ddd', borderRadius: 8, padding: '1rem' }}>
          <legend>Job</legend>
          {!creatingJob ? (
            <div style={{ display: 'flex', gap: '0.5rem', alignItems: 'center' }}>
              <select value={jobId} onChange={(e) => setJobId(e.target.value)} style={{ flex: 1, padding: '0.4rem' }}>
                {jobs.length === 0 && <option value="">No open jobs</option>}
                {jobs.map((j) => (
                  <option key={j.id} value={j.id}>
                    {j.ticketNumber} — {j.title} ({j.status})
                  </option>
                ))}
              </select>
              <button type="button" onClick={() => setCreatingJob(true)}>
                + New job
              </button>
              {selectedJob && (
                <Link href={`/jobs/${selectedJob.id}`} target="_blank">
                  View map ↗
                </Link>
              )}
            </div>
          ) : (
            <form onSubmit={createJob} style={{ display: 'flex', gap: '0.5rem' }}>
              <input
                placeholder="ticket number"
                value={newTicket}
                onChange={(e) => setNewTicket(e.target.value)}
                required
                style={{ padding: '0.4rem' }}
              />
              <input
                placeholder="title"
                value={newTitle}
                onChange={(e) => setNewTitle(e.target.value)}
                required
                style={{ padding: '0.4rem', flex: 1 }}
              />
              <button type="submit">Create</button>
              <button type="button" onClick={() => setCreatingJob(false)}>
                Cancel
              </button>
            </form>
          )}
        </fieldset>

        <fieldset style={{ marginBottom: '1rem', border: '1px solid #ddd', borderRadius: 8, padding: '1rem' }}>
          <legend>Transmitter</legend>
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '0.75rem' }}>
            <label>
              Serial number
              <input value={serial} onChange={(e) => setSerial(e.target.value)} style={{ width: '100%', padding: '0.4rem' }} />
            </label>
            <label>
              Utility type
              <select value={utility} onChange={(e) => setUtility(e.target.value)} style={{ width: '100%', padding: '0.4rem' }}>
                {UTILITIES.map((u) => (
                  <option key={u} value={u}>
                    {u}
                  </option>
                ))}
              </select>
            </label>
            <label>
              GPS fix type
              <select value={fixType} onChange={(e) => setFixType(e.target.value)} style={{ width: '100%', padding: '0.4rem' }}>
                {FIX_TYPES.map((f) => (
                  <option key={f} value={f}>
                    {f}
                  </option>
                ))}
              </select>
            </label>
            <label>
              Locate mode
              <select value={locateMode} onChange={(e) => setLocateMode(e.target.value)} style={{ width: '100%', padding: '0.4rem' }}>
                {LOCATE_MODES.map((m) => (
                  <option key={m} value={m}>
                    {m}
                  </option>
                ))}
              </select>
            </label>
            <label>
              Depth (m)
              <input
                type="number"
                step="0.1"
                value={depth}
                onChange={(e) => setDepth(Number(e.target.value))}
                style={{ width: '100%', padding: '0.4rem' }}
              />
            </label>
            <label>
              Frequency (Hz)
              <input
                type="number"
                value={frequencyHz}
                onChange={(e) => setFrequencyHz(Number(e.target.value))}
                style={{ width: '100%', padding: '0.4rem' }}
              />
            </label>
            <label>
              Current (mA)
              <input
                type="number"
                step="0.1"
                value={currentMa}
                onChange={(e) => setCurrentMa(Number(e.target.value))}
                style={{ width: '100%', padding: '0.4rem' }}
              />
            </label>
          </div>
        </fieldset>

        <fieldset style={{ marginBottom: '1rem', border: '1px solid #ddd', borderRadius: 8, padding: '1rem' }}>
          <legend>Path</legend>
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr 1fr', gap: '0.75rem' }}>
            <label>
              Start lat
              <input
                type="number"
                step="0.0000001"
                value={originLat}
                onChange={(e) => setOriginLat(Number(e.target.value))}
                style={{ width: '100%', padding: '0.4rem' }}
              />
            </label>
            <label>
              Start lng
              <input
                type="number"
                step="0.0000001"
                value={originLng}
                onChange={(e) => setOriginLng(Number(e.target.value))}
                style={{ width: '100%', padding: '0.4rem' }}
              />
            </label>
            <label>
              Heading (°)
              <input
                type="number"
                value={headingDeg}
                onChange={(e) => setHeadingDeg(Number(e.target.value))}
                style={{ width: '100%', padding: '0.4rem' }}
              />
            </label>
            <label>
              Step (m)
              <input
                type="number"
                step="0.1"
                value={stepMeters}
                onChange={(e) => setStepMeters(Number(e.target.value))}
                style={{ width: '100%', padding: '0.4rem' }}
              />
            </label>
          </div>
        </fieldset>

        <div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem', marginBottom: '1rem' }}>
          <button onClick={sendPoint} disabled={!jobId || autoSending} style={{ padding: '0.6rem 1rem' }}>
            Send {messageType === 'log' ? 'log point' : 'status update'}
          </button>
          <button onClick={toggleAutoSend} disabled={!jobId} style={{ padding: '0.6rem 1rem' }}>
            {autoSending ? 'Stop auto-send' : `Start auto-send (${messageType})`}
          </button>
          <label>
            every{' '}
            <input
              type="number"
              min={1}
              value={intervalSec}
              onChange={(e) => setIntervalSec(Number(e.target.value))}
              style={{ width: 50, padding: '0.3rem' }}
            />{' '}
            s
          </label>
          <button onClick={reset} style={{ marginLeft: 'auto' }}>
            Reset path
          </button>
          <span style={{ color: '#666' }}>{count} sent this session</span>
        </div>

        <table style={{ width: '100%', borderCollapse: 'collapse' }}>
          <thead>
            <tr style={{ textAlign: 'left', borderBottom: '2px solid #ddd' }}>
              <th style={{ padding: '0.4rem' }}>Time</th>
              <th>Seq</th>
              <th>Type</th>
              <th>Lat</th>
              <th>Lng</th>
              <th>Depth</th>
              <th>Via</th>
            </tr>
          </thead>
          <tbody>
            {sent.map((s) => (
              <tr key={s.seq} style={{ borderBottom: '1px solid #eee' }}>
                <td style={{ padding: '0.4rem' }}>{s.at}</td>
                <td>{s.seq}</td>
                <td style={{ color: s.type === 'log' ? '#333' : '#1a73e8' }}>{s.type}</td>
                <td>{s.lat.toFixed(7)}</td>
                <td>{s.lng.toFixed(7)}</td>
                <td>{s.depth} m</td>
                <td style={{ color: s.transport === 'mqtts' ? '#2e7d32' : '#888' }}>{s.transport}</td>
              </tr>
            ))}
            {sent.length === 0 && (
              <tr>
                <td colSpan={7} style={{ padding: '1.5rem', textAlign: 'center', color: '#888' }}>
                  No points sent yet.
                </td>
              </tr>
            )}
          </tbody>
        </table>
      </main>
    </div>
  );
}
