import React, { useCallback, useEffect, useRef, useState } from 'react';
import { useNavigate } from 'react-router-dom';

import { AnswerBlock } from '../components/signup-mirror/answer-block';
import { MirrorFooter } from '../components/signup-mirror/mirror-footer';
import { MirrorHeader } from '../components/signup-mirror/mirror-header';
import { MirrorTopBar } from '../components/signup-mirror/mirror-topbar';
import { MoneyMakerBlock } from '../components/signup-mirror/money-maker-block';
import { SANS } from '../components/signup-mirror/theme';
import { Spinner } from '../components/ui/Spinner';
import { useAppStateContext } from '../context/user.data.context';
import { MirrorPayload } from '../service/company-id/company-id.interface';
import {
  confirmCompanyIdMirror,
  dispatchCompanyIdMirrorScan,
  getCompanyIdMirror,
} from '../service/company-id/company-id.service';

const SCAN_POLL_MS = 4000;
const WORK_TIMEOUT_MS = 25000;

type AnswerState = Record<string, { value: string; confirmed: boolean }>;

const PageLoading = (): JSX.Element => (
  <div className="flex min-h-[60vh] items-center justify-center">
    <Spinner className="h-6 w-6 text-gray-400" />
  </div>
);

const SignupMirror = (): JSX.Element => {
  const navigate = useNavigate();
  const { token, clientId } = useAppStateContext();

  const [mirror, setMirror] = useState<MirrorPayload | null>(null);
  const [loadingMirror, setLoadingMirror] = useState(true);
  const [workTimedOut, setWorkTimedOut] = useState(false);
  const [answers, setAnswers] = useState<AnswerState>({});
  const [money, setMoney] = useState<string[]>(['', '', '']);
  const [showHelper, setShowHelper] = useState(false);
  const [saving, setSaving] = useState(false);

  const seededRef = useRef(false);
  const scanDispatchedRef = useRef(false);
  const textareas = useRef<Record<string, HTMLTextAreaElement | null>>({});

  const blocks = mirror?.blocks ?? [];
  const scanning = !!mirror && !mirror.scanned && !workTimedOut;
  const populating = !!mirror && !mirror.populated && !workTimedOut;
  const working = scanning || populating;

  const refreshMirror = useCallback(async () => {
    if (!clientId || !token) return;
    try {
      const fresh = await getCompanyIdMirror(clientId, token);
      setMirror(fresh);
    } catch (error) {
      // keep the last snapshot on transient errors
      console.error(error);
    }
  }, [clientId, token]);

  // Initial load - render immediately once the mirror arrives instead of
  // gating the whole screen behind a spinner.
  useEffect(() => {
    if (!clientId || !token) return undefined;
    let cancelled = false;
    (async () => {
      try {
        const fresh = await getCompanyIdMirror(clientId, token);
        if (cancelled) return;
        setMirror(fresh);
        if (!seededRef.current) {
          seededRef.current = true;
          setAnswers(
            Object.fromEntries(
              fresh.blocks.map(block => [
                block.field_id,
                { value: block.value, confirmed: false },
              ])
            )
          );
          setMoney(fresh.money.length === 3 ? fresh.money : ['', '', '']);
        }
      } catch (error) {
        console.error(error);
      } finally {
        if (!cancelled) setLoadingMirror(false);
      }
    })();

    return () => {
      cancelled = true;
    };
  }, [clientId, token]);

  // Fire the scan exactly once, guarded by a ref (survives re-renders without
  // re-triggering, unlike a plain state flag which could race with polling).
  useEffect(() => {
    if (!scanning || scanDispatchedRef.current || !clientId || !token) return;
    scanDispatchedRef.current = true;
    dispatchCompanyIdMirrorScan(clientId, token).catch(error => {
      console.error(error);
    });
  }, [scanning, clientId, token]);

  // Poll while working, stop at scanned && populated or the 25s hard cap.
  useEffect(() => {
    if (!working) return undefined;
    const timer = setInterval(refreshMirror, SCAN_POLL_MS);

    return () => clearInterval(timer);
  }, [working, refreshMirror]);

  useEffect(() => {
    if (!mirror || (mirror.scanned && mirror.populated)) return undefined;
    const timer = setTimeout(() => setWorkTimedOut(true), WORK_TIMEOUT_MS);

    return () => clearTimeout(timer);
  }, [mirror]);

  // Fill in freshly-populated site-read values without clobbering edits the
  // merchant has already started typing.
  useEffect(() => {
    const currentBlocks = mirror?.blocks ?? [];
    setAnswers(prev => {
      let changed = false;
      const next = { ...prev };
      for (const block of currentBlocks) {
        if (!prev[block.field_id]?.value && block.value) {
          next[block.field_id] = {
            value: block.value,
            confirmed: prev[block.field_id]?.confirmed ?? false,
          };
          changed = true;
        }
      }

      return changed ? next : prev;
    });
  }, [mirror]);

  const confirmedCount = blocks.filter(
    block => answers[block.field_id]?.confirmed
  ).length;
  const done = blocks.length > 0 && confirmedCount === blocks.length;
  const moneyFilled = money.some(entry => entry.trim().length > 0);
  const ctaEnabled = done && moneyFilled && !saving;

  const setValue = (key: string, value: string) =>
    setAnswers(prev => ({ ...prev, [key]: { ...prev[key], value } }));

  const confirm = (key: string) =>
    setAnswers(prev =>
      prev[key]?.value.trim()
        ? { ...prev, [key]: { ...prev[key], confirmed: true } }
        : prev
    );

  const reopen = (key: string) =>
    setAnswers(prev => ({
      ...prev,
      [key]: { ...prev[key], confirmed: false },
    }));

  const fixIt = (key: string) => {
    reopen(key);
    const node = textareas.current[key];
    if (node) node.focus();
  };

  const setMoneyAt = (index: number, value: string) =>
    setMoney(prev =>
      prev.map((entry, entryIndex) => (entryIndex === index ? value : entry))
    );

  const onCta = async (): Promise<void> => {
    if (saving) return;
    if (!ctaEnabled) {
      setShowHelper(true);

      return;
    }
    if (!clientId || !token) return;

    const values = Object.fromEntries(
      blocks.map(block => [block.field_id, answers[block.field_id].value])
    );

    setSaving(true);
    try {
      await confirmCompanyIdMirror(clientId, token, values, money);
      navigate('/launch-discount');
    } catch (error) {
      console.error(error);
      setSaving(false);
    }
  };

  if (!token || !clientId || loadingMirror) {
    return <PageLoading />;
  }

  if (!mirror) {
    return (
      <div
        className={`min-h-[60vh] flex items-center justify-center text-[#8c9196] ${SANS}`}
      >
        The Signup Mirror could not be loaded. Refresh to retry.
      </div>
    );
  }

  return (
    <div
      className={`min-h-screen bg-[#f6f6f7] text-[#202223] ${SANS} text-[14px] leading-[1.55] antialiased selection:bg-[rgba(200,23,93,0.2)]`}
    >
      <MirrorTopBar />

      <div className="max-w-[1200px] mx-auto px-6 pt-9">
        <MirrorHeader done={done} />

        <div className="mt-6 grid grid-cols-1 md:grid-cols-2 gap-3 items-stretch">
          {blocks.map(block => (
            <AnswerBlock
              key={block.field_id}
              block={block}
              value={answers[block.field_id]?.value ?? ''}
              confirmed={answers[block.field_id]?.confirmed ?? false}
              populating={populating}
              onChange={value => setValue(block.field_id, value)}
              onConfirm={() => confirm(block.field_id)}
              onFixIt={() => fixIt(block.field_id)}
              onReopen={() => reopen(block.field_id)}
              registerTextarea={node => {
                textareas.current[block.field_id] = node;
              }}
            />
          ))}

          <MoneyMakerBlock money={money} onChangeAt={setMoneyAt} />
        </div>
      </div>

      <MirrorFooter
        blocks={blocks}
        confirmedById={Object.fromEntries(
          blocks.map(block => [
            block.field_id,
            !!answers[block.field_id]?.confirmed,
          ])
        )}
        working={working}
        confirmedCount={confirmedCount}
        ctaEnabled={ctaEnabled}
        saving={saving}
        showHelper={showHelper}
        onCta={onCta}
        onHelperChange={setShowHelper}
      />
    </div>
  );
};

export default SignupMirror;
