// Settings — notifications & link modals (split from settings.jsx).
// NotificationChannels, Daily/ApptReminder/EmClick testers, Toggle,
// FieldLabel, colorTint, Telegram/Partner link modals.

// Daily morning summary tester — preview today's LINE message + fire a test
// Global on/off switches for each notification channel (LINE / Telegram).
// Stored in notification_settings (single row); every push edge function
// reads it before sending. Only the super-admin can change it.
function NotificationChannels({ currentUser }) {
  const isSuperAdmin = currentUser?.abo_code === '7014634134';
  const [cfg, setCfg] = useState({ line_enabled: false, telegram_enabled: true });
  const [loading, setLoading] = useState(true);
  const [savingCh, setSavingCh] = useState(null); // 'line' | 'telegram'
  const [status, setStatus] = useState(null);

  useEffect(() => {
    (async () => {
      if (!window.SB_READY || !window.db?.notifSettings) { setLoading(false); return; }
      const { data } = await window.db.notifSettings.get();
      if (data) setCfg({ line_enabled: data.line_enabled !== false, telegram_enabled: data.telegram_enabled !== false });
      setLoading(false);
    })();
  }, []);

  const toggle = async (ch) => {
    if (!isSuperAdmin) return;
    const key = ch === 'line' ? 'line_enabled' : 'telegram_enabled';
    const next = { ...cfg, [key]: !cfg[key] };
    setCfg(next); setSavingCh(ch); setStatus(null);
    const { error } = await window.db.notifSettings.save({ line_enabled: next.line_enabled, telegram_enabled: next.telegram_enabled });
    setSavingCh(null);
    if (error) { setCfg(cfg); setStatus({ ok: false, msg: 'บันทึกไม่สำเร็จ: ' + error.message }); }
    else setStatus({ ok: true, msg: 'บันทึกแล้ว · มีผลกับการแจ้งเตือนทั้งระบบ' });
  };

  const Row = ({ ch, label, desc, color, logo }) => {
    const on = cfg[ch === 'line' ? 'line_enabled' : 'telegram_enabled'];
    return (
      <div className="flex items-center gap-3 p-3 rounded-xl border border-ink-100">
        <div className="w-10 h-10 rounded-lg flex items-center justify-center text-white font-bold flex-shrink-0" style={{ background: color }}>{logo}</div>
        <div className="flex-1 min-w-0">
          <div className="flex items-center gap-2">
            <span className="text-sm font-semibold text-ink-900">{label}</span>
            <Pill color={on ? '#10B981' : '#94A3B8'} tint={on ? '#E7F8F1' : '#F1F5F9'} size="xs">{on ? 'เปิด' : 'ปิด'}</Pill>
          </div>
          <div className="text-[11px] text-ink-500 mt-0.5">{desc}</div>
        </div>
        <button onClick={() => toggle(ch)} disabled={!isSuperAdmin || savingCh === ch}
          className={`relative w-11 h-6 rounded-full transition-colors flex-shrink-0 ${!isSuperAdmin ? 'opacity-50 cursor-not-allowed' : ''} ${on ? 'bg-emerald-500' : 'bg-ink-300'}`}>
          <span className={`absolute top-0.5 left-0.5 w-5 h-5 rounded-full bg-white shadow transition-transform ${on ? 'translate-x-5' : ''}`} />
        </button>
      </div>
    );
  };

  return (
    <Card pad={false}>
      <div className="px-5 pt-4 pb-3 border-b border-ink-100 flex items-center gap-2.5">
        <div className="w-8 h-8 rounded-md bg-gradient-to-br from-emerald-500 to-sky-500 text-white flex items-center justify-center"><Icon name="bell" className="w-4 h-4" /></div>
        <div className="flex-1 min-w-0">
          <h3 className="text-sm font-semibold text-ink-900">ช่องทางการแจ้งเตือน</h3>
          <p className="text-[11px] text-ink-400">เปิด/ปิดการส่งแยกแต่ละช่องทาง · มีผลกับการแจ้งเตือนทั้งระบบ (ทุก account)</p>
        </div>
      </div>
      <div className="p-5 space-y-3">
        {loading ? <div className="py-6 text-center text-xs text-ink-400">กำลังโหลด...</div> : (
          <>
            <Row ch="line" label="LINE OA" desc="แจ้งเตือนผ่าน LINE Official Account" color="#06C755" logo="L" />
            <Row ch="telegram" label="Telegram" desc="แจ้งเตือนผ่าน Telegram bot" color="#229ED9" logo="T" />
            {status && <div className={`text-[11px] rounded px-3 py-2 ${status.ok ? 'bg-emerald-50 text-emerald-700 border border-emerald-100' : 'bg-rose-50 text-rose-700 border border-rose-100'}`}>{status.msg}</div>}
            {!isSuperAdmin && <div className="text-[11px] text-ink-400">ℹ︎ เฉพาะผู้ดูแลระบบเท่านั้นที่ปรับได้ · แสดงสถานะปัจจุบันให้ดู</div>}
          </>
        )}
      </div>
    </Card>
  );
}

// push on demand. The cron at 07:00 Bangkok hits the same edge function with
// no userId so every user with a linked LINE gets the same kind of message.
function DailyPushTester({ user }) {
  const [busy, setBusy] = useState(false);
  const [preview, setPreview] = useState('');
  const [result, setResult] = useState(null); // null | { ok, results, error }

  const fnUrl = (window.EMPHASIS_ENV?.supabaseUrl || '') + '/functions/v1/daily-summary-push';
  const apikey = window.EMPHASIS_ENV?.supabaseAnonKey || '';

  const call = async (body) => {
    setBusy(true);
    setResult(null);
    try {
      // Forward the user's Supabase session so the edge function counts as
      // an authenticated invocation (and we get useful logs).
      const { data: { session } = {} } = await window.supabase.auth.getSession();
      const res = await fetch(fnUrl, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          apikey,
          Authorization: session?.access_token ? `Bearer ${session.access_token}` : `Bearer ${apikey}`,
        },
        body: JSON.stringify(body),
      });
      const data = await res.json();
      return data;
    } catch (e) {
      return { error: e.message };
    } finally {
      setBusy(false);
    }
  };

  const handlePreview = async () => {
    if (!user?.id) return;
    const data = await call({ userId: user.id, previewOnly: true });
    if (data.error) { setResult({ ok: false, error: data.error }); return; }
    const text = data.results?.[0]?.message || '(no message)';
    setPreview(text);
    setResult({ ok: true, results: data.results, mode: 'preview' });
  };

  const handleSend = async () => {
    if (!user?.id) return;
    if (!confirm('ส่ง LINE สรุปวันนี้ไปยัง LINE ของคุณ (และคู่ร่วมธุรกิจถ้ามี) ตอนนี้เลย?')) return;
    const data = await call({ userId: user.id, test: true });
    if (data.error) { setResult({ ok: false, error: data.error }); return; }
    const text = data.results?.[0]?.message || '';
    if (text) setPreview(text);
    setResult({ ok: true, results: data.results, mode: 'test' });
  };

  const hasLine = !!user?.line_user_id;

  return (
    <div className="space-y-4">
      <Card title="แจ้งเตือนสรุปวันเช้า (Daily Morning Summary)">
        <div className="space-y-3">
          <div className="flex items-start gap-3 p-3 rounded-xl bg-emerald-50/60 border border-emerald-200/60">
            <div className="w-10 h-10 rounded-lg bg-emerald-500 text-white flex items-center justify-center flex-shrink-0">
              <Icon name="bell" className="w-5 h-5" />
            </div>
            <div className="flex-1 min-w-0">
              <div className="text-sm font-semibold text-ink-900">ส่งสรุปนัดวันนี้ผ่าน LINE ทุก 07:00 น.</div>
              <div className="text-[11px] text-ink-600 mt-0.5 leading-relaxed">
                ระบบจะ push สรุปนัดของวันนั้น (ส่วนตัว · คู่ร่วมธุรกิจ · DL) ไปยัง LINE ที่ผูกไว้
                ทุกเช้า เวลา 07:00 น. (ตามเวลาประเทศไทย)
                <br />
                <span className="text-purple-700 font-medium">• จันทร์</span> แนบรายชื่อคนที่ <b>ผ่าน BM + BI แล้วแต่ยังเข้า EM Begin ไม่ครบ</b>
                <br />
                <span className="text-pink-700 font-medium">• พฤหัส</span> แนบรายชื่อคนที่ <b>ต้องเข้า EM Click</b>
              </div>
              <div className="text-[10px] text-ink-500 mt-1.5 flex items-center gap-2 flex-wrap">
                <span>LINE หลัก:</span>
                {hasLine
                  ? <span className="text-emerald-700 font-semibold">✓ ผูกแล้ว · {user?.line_display_name || user?.name}</span>
                  : <span className="text-rose-600">ยังไม่ได้ผูก — ไปที่ Login & Security → "เชื่อม LINE"</span>}
                {user?.partner_line_user_id && (
                  <span className="text-emerald-700 font-semibold">· คู่: ✓ {user.partner_line_display_name || user.partner_name}</span>
                )}
              </div>
            </div>
          </div>

          <div className="flex items-center gap-2 flex-wrap">
            <Btn variant="ghost" size="sm" onClick={handlePreview} disabled={busy || !user?.id}
              icon={<Icon name="eye" className="w-3.5 h-3.5" />}>
              ดูข้อความตัวอย่าง
            </Btn>
            <Btn variant="brand" size="sm" onClick={handleSend} disabled={busy || !hasLine}
              icon={<Icon name="line" className="w-3.5 h-3.5" />}>
              {busy ? 'กำลังส่ง...' : 'ทดสอบส่ง LINE ตอนนี้'}
            </Btn>
            {!hasLine && (
              <span className="text-[11px] text-rose-600">กรุณาเชื่อม LINE ก่อนถึงจะทดสอบส่งได้</span>
            )}
          </div>

          {preview && (
            <div>
              <div className="text-[10px] uppercase tracking-wider font-semibold text-ink-500 mb-1.5">
                ตัวอย่างข้อความ (จะถูกส่งจริงเวลา 07:00 น.)
              </div>
              <pre className="text-[11px] leading-relaxed whitespace-pre-wrap bg-ink-900 text-emerald-300 rounded-xl p-4 font-mono max-h-80 overflow-y-auto"
                   style={{ fontFamily: 'ui-monospace, SFMono-Regular, Menlo, monospace' }}>
                {preview}
              </pre>
            </div>
          )}

          {result && (
            <div className={`rounded-lg p-3 text-[11px] ${result.ok ? 'bg-emerald-50 border border-emerald-200 text-emerald-800' : 'bg-rose-50 border border-rose-200 text-rose-700'}`}>
              {result.ok ? (
                <>
                  <div className="font-semibold">
                    {result.mode === 'preview' ? '✓ ดึงข้อมูลเรียบร้อย' : '✓ ส่ง LINE เรียบร้อย'}
                  </div>
                  {result.results?.map((r, i) => (
                    <div key={i} className="mt-1 num">
                      → {r.name}: {r.count} เคส {r.ok ? '✓' : `✗ ${r.error || ''}`}
                    </div>
                  ))}
                </>
              ) : (
                <>
                  <div className="font-semibold">✗ ส่งไม่สำเร็จ</div>
                  <div className="mt-1">{result.error}</div>
                </>
              )}
            </div>
          )}
        </div>
      </Card>

      <ApptReminderTester user={user} />
      {/* EmClickReminderTester removed — Thursday EM Click chase list is
          now appended to the daily summary push above. Use the daily
          summary "ทดสอบส่ง LINE ตอนนี้" to preview the merged message
          (test mode loads both Mon + Thu sections so you can see both). */}

      <Card title="วิธีตั้ง cron" headerClass="">
        <details className="group">
          <summary className="cursor-pointer text-[12px] font-semibold text-ink-700 hover:text-brand-600">
            ดู SQL สำหรับ pg_cron ▾
          </summary>
          <pre className="mt-3 text-[10px] leading-relaxed bg-ink-50 rounded-lg p-3 font-mono overflow-x-auto whitespace-pre"
               style={{ fontFamily: 'ui-monospace, SFMono-Regular, Menlo, monospace' }}>
{`-- รันใน Supabase SQL Editor (ครั้งเดียว). 00:00 UTC = 07:00 Asia/Bangkok.
create extension if not exists pg_cron;
create extension if not exists pg_net;

-- เก็บ service-role key ใน vault เพื่อให้ cron job เรียก edge function ได้
select vault.create_secret(
  '<your-supabase-service-role-key>',
  'service_role_key'
);

-- 1) Daily summary 07:00 น. (Asia/Bangkok)
select cron.schedule(
  'daily-summary-push-7am-bkk',
  '0 0 * * *',  -- 00:00 UTC = 07:00 Asia/Bangkok
  $$
    select net.http_post(
      url := '${fnUrl}',
      headers := jsonb_build_object(
        'Content-Type', 'application/json',
        'Authorization', 'Bearer ' || (select decrypted_secret from vault.decrypted_secrets where name='service_role_key')
      ),
      body := '{}'::jsonb
    );
  $$
);

-- 2) Appt reminder — เตือนล่วงหน้า 30 นาที (cron ทุก 5 นาที)
select cron.schedule(
  'appt-reminder-push-every-5min',
  '*/5 * * * *',
  $$
    select net.http_post(
      url := '${(window.EMPHASIS_ENV?.supabaseUrl || '')}/functions/v1/appt-reminder-push',
      headers := jsonb_build_object(
        'Content-Type', 'application/json',
        'Authorization', 'Bearer ' || (select decrypted_secret from vault.decrypted_secrets where name='service_role_key')
      ),
      body := '{}'::jsonb
    );
  $$
);`}
          </pre>
          <div className="mt-2 text-[10px] text-ink-500 leading-relaxed">
            อย่าลืม:&nbsp;<code className="text-[10px] bg-ink-50 px-1 rounded">supabase secrets set LINE_MESSAGING_TOKEN=&lt;OA access token&gt;</code>
          </div>
        </details>
      </Card>
    </div>
  );
}

// 30-minute pre-appointment LINE reminder tester. Loads the user's next
// upcoming pending/followup appts and lets them fire a test push for any
// one of them — same logic the every-5-min cron uses, just with the
// reminded_at guard bypassed so they can test repeatedly.
function ApptReminderTester({ user }) {
  const [appts, setAppts] = useState([]);
  const [busyId, setBusyId] = useState(null);
  const [preview, setPreview] = useState('');
  const [result, setResult] = useState(null);
  const [loading, setLoading] = useState(true);

  const fnUrl = (window.EMPHASIS_ENV?.supabaseUrl || '') + '/functions/v1/appt-reminder-push';
  const apikey = window.EMPHASIS_ENV?.supabaseAnonKey || '';
  const hasLine = !!user?.line_user_id;

  // Load this owner's upcoming appts (next 7 days, pending/followup).
  useEffect(() => {
    if (!user?.id || !window.SB_READY) { setLoading(false); return; }
    let cancelled = false;
    (async () => {
      const nowIso = new Date().toISOString();
      const weekIso = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString();
      const { data, error } = await window.supabase
        .from('appointments')
        .select(`id, scheduled_at, duration_minutes, type_id, status, reminded_at,
                 prospect:prospects!appointments_prospect_id_fkey(name),
                 dl_prospect:prospects!appointments_dl_prospect_id_fkey(name),
                 type:appt_type(label, full_name)`)
        .eq('owner_id', user.id)
        .gte('scheduled_at', nowIso)
        .lte('scheduled_at', weekIso)
        .in('status', ['pending', 'followup'])
        .order('scheduled_at')
        .limit(10);
      if (cancelled) return;
      if (!error) setAppts(data || []);
      setLoading(false);
    })();
    return () => { cancelled = true; };
  }, [user?.id]);

  const callFn = async (body) => {
    const { data: { session } = {} } = await window.supabase.auth.getSession();
    const res = await fetch(fnUrl, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        apikey,
        Authorization: session?.access_token ? `Bearer ${session.access_token}` : `Bearer ${apikey}`,
      },
      body: JSON.stringify(body),
    });
    return res.json();
  };

  const handlePreview = async (apptId) => {
    setBusyId(apptId); setResult(null);
    try {
      const data = await callFn({ apptId, previewOnly: true });
      if (data.error) { setResult({ ok: false, error: data.error }); return; }
      setPreview(data.results?.[0]?.message || '');
      setResult({ ok: true, mode: 'preview' });
    } finally { setBusyId(null); }
  };

  const handleSend = async (apptId, name) => {
    if (!confirm(`ส่ง LINE เตือนนัด "${name}" ไปยัง LINE ของคุณ (และคู่ถ้ามี) ตอนนี้?`)) return;
    setBusyId(apptId); setResult(null);
    try {
      const data = await callFn({ apptId, test: true });
      if (data.error) { setResult({ ok: false, error: data.error }); return; }
      const text = data.results?.[0]?.message || '';
      if (text) setPreview(text);
      setResult({ ok: data.results?.[0]?.ok !== false, mode: 'test', results: data.results });
    } finally { setBusyId(null); }
  };

  const fmtTime = (iso) => {
    const d = new Date(iso);
    return d.toLocaleTimeString('th-TH', { hour: '2-digit', minute: '2-digit', timeZone: 'Asia/Bangkok' });
  };
  const fmtDate = (iso) => {
    const d = new Date(iso);
    const today = new Date();
    const sameDay = d.toDateString() === today.toDateString();
    if (sameDay) return 'วันนี้';
    const tomorrow = new Date(today.getTime() + 24*60*60*1000);
    if (d.toDateString() === tomorrow.toDateString()) return 'พรุ่งนี้';
    return d.toLocaleDateString('th-TH', { weekday: 'short', day: 'numeric', month: 'short', timeZone: 'Asia/Bangkok' });
  };

  return (
    <Card title="แจ้งเตือนก่อนนัด 30 นาที (Appointment Reminder)">
      <div className="space-y-3">
        <div className="flex items-start gap-3 p-3 rounded-xl bg-amber-50/60 border border-amber-200/60">
          <div className="w-10 h-10 rounded-lg bg-amber-500 text-white flex items-center justify-center flex-shrink-0">
            <Icon name="clock" className="w-5 h-5" />
          </div>
          <div className="flex-1 min-w-0">
            <div className="text-sm font-semibold text-ink-900">ส่ง LINE เตือนก่อนเวลานัด 30 นาที</div>
            <div className="text-[11px] text-ink-600 mt-0.5 leading-relaxed">
              ระบบจะ scan ทุก 5 นาที — เมื่อเหลือเวลาก่อนนัด ~30 นาที จะ push เตือนไปยัง LINE ของเจ้าของเคส
              (และคู่ร่วมธุรกิจถ้ามี) อัตโนมัติ ส่งครั้งเดียวต่อนัด
            </div>
          </div>
        </div>

        <div className="text-[10px] uppercase tracking-wider font-semibold text-ink-500">
          นัดที่กำลังจะมาถึง — เลือกเพื่อทดสอบ
        </div>

        {loading ? (
          <div className="text-[11px] text-ink-400 py-3 text-center">กำลังโหลด...</div>
        ) : appts.length === 0 ? (
          <div className="text-[11px] text-ink-400 py-3 text-center">— ไม่มีนัดที่กำลังจะมาถึง —</div>
        ) : (
          <div className="space-y-1.5 max-h-64 overflow-y-auto scroll-thin">
            {appts.map(a => {
              const isBusy = busyId === a.id;
              const isReminded = !!a.reminded_at;
              return (
                <div key={a.id} className="flex items-center gap-2 p-2 rounded-lg border border-ink-100 bg-white">
                  <div className="w-12 h-12 rounded-md flex flex-col items-center justify-center text-white text-[10px] font-bold flex-shrink-0"
                       style={{ background: '#2F5DE5' }}>
                    <div>{a.type?.label || '?'}</div>
                    <div className="text-[9px] num leading-none mt-0.5">{fmtTime(a.scheduled_at)}</div>
                  </div>
                  <div className="flex-1 min-w-0">
                    <div className="text-xs font-semibold text-ink-900 truncate">{a.prospect?.name || '—'}</div>
                    <div className="text-[10px] text-ink-500 num flex items-center gap-1.5">
                      <span>{fmtDate(a.scheduled_at)}</span>
                      {isReminded && <Pill color="#10B981" tint="#E7F8F1" size="xs">✓ เตือนแล้ว</Pill>}
                    </div>
                  </div>
                  <div className="flex items-center gap-1 flex-shrink-0">
                    <Btn variant="ghost" size="sm" disabled={isBusy}
                      onClick={() => handlePreview(a.id)}
                      title="ดูตัวอย่างข้อความ">
                      <Icon name="eye" className="w-3.5 h-3.5" />
                    </Btn>
                    <Btn variant="brand" size="sm" disabled={isBusy || !hasLine}
                      onClick={() => handleSend(a.id, a.prospect?.name || 'นัด')}
                      title="ทดสอบส่ง LINE เตือน">
                      {isBusy ? '...' : 'ทดสอบเตือน'}
                    </Btn>
                  </div>
                </div>
              );
            })}
          </div>
        )}

        {preview && (
          <div>
            <div className="text-[10px] uppercase tracking-wider font-semibold text-ink-500 mb-1.5">ตัวอย่างข้อความเตือน</div>
            <pre className="text-[11px] leading-relaxed whitespace-pre-wrap bg-ink-900 text-amber-200 rounded-xl p-4 font-mono max-h-60 overflow-y-auto"
                 style={{ fontFamily: 'ui-monospace, SFMono-Regular, Menlo, monospace' }}>
              {preview}
            </pre>
          </div>
        )}

        {result && (
          <div className={`rounded-lg p-3 text-[11px] ${result.ok ? 'bg-emerald-50 border border-emerald-200 text-emerald-800' : 'bg-rose-50 border border-rose-200 text-rose-700'}`}>
            {result.ok
              ? <>✓ {result.mode === 'preview' ? 'ดึงตัวอย่างเรียบร้อย' : 'ส่ง LINE เตือนเรียบร้อย'}</>
              : <>✗ {result.error || 'ส่งไม่สำเร็จ'}</>}
          </div>
        )}
      </div>
    </Card>
  );
}

// Weekly Thursday 7am EM Click reminder tester. Mirrors the daily-summary
// tester pattern: preview the message, then fire a test push to the
// current user (and partner) on demand. Cron at 0 0 * * 4 (00:00 UTC =
// 07:00 Asia/Bangkok) hits the same edge function with no body.
function EmClickReminderTester({ user }) {
  const [busy, setBusy] = useState(false);
  const [preview, setPreview] = useState('');
  const [result, setResult] = useState(null);

  const fnUrl = (window.EMPHASIS_ENV?.supabaseUrl || '') + '/functions/v1/em-click-reminder-push';
  const apikey = window.EMPHASIS_ENV?.supabaseAnonKey || '';
  const hasLine = !!user?.line_user_id;

  const call = async (body) => {
    setBusy(true);
    setResult(null);
    try {
      const { data: { session } = {} } = await window.supabase.auth.getSession();
      const res = await fetch(fnUrl, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          apikey,
          Authorization: session?.access_token ? `Bearer ${session.access_token}` : `Bearer ${apikey}`,
        },
        body: JSON.stringify(body),
      });
      return await res.json();
    } catch (e) { return { error: e.message }; }
    finally { setBusy(false); }
  };

  const handlePreview = async () => {
    if (!user?.id) return;
    const data = await call({ userId: user.id, previewOnly: true });
    if (data.error) { setResult({ ok: false, error: data.error }); return; }
    setPreview(data.results?.[0]?.message || '');
    setResult({ ok: true, results: data.results, mode: 'preview' });
  };

  const handleSend = async () => {
    if (!user?.id) return;
    if (!confirm('ส่ง LINE เตือนคนที่ควรเข้า EM Click ไปยัง LINE ของคุณ (และคู่ถ้ามี) ตอนนี้เลย?')) return;
    const data = await call({ userId: user.id, test: true });
    if (data.error) { setResult({ ok: false, error: data.error }); return; }
    const text = data.results?.[0]?.message || '';
    if (text) setPreview(text);
    setResult({ ok: true, results: data.results, mode: 'test' });
  };

  return (
    <Card title="แจ้งเตือน EM Click (ทุกพฤหัส 07:00)">
      <div className="space-y-3">
        <div className="flex items-start gap-3 p-3 rounded-xl bg-purple-50/60 border border-purple-200/60">
          <div className="w-10 h-10 rounded-lg bg-purple-500 text-white flex items-center justify-center flex-shrink-0">
            <span className="text-lg leading-none">🎯</span>
          </div>
          <div className="flex-1 min-w-0">
            <div className="text-sm font-semibold text-ink-900">เตือนคนที่ควรเข้า EM Click ทุกพฤหัส 07:00 น.</div>
            <div className="text-[11px] text-ink-600 mt-0.5 leading-relaxed">
              ระบบจะ scan ABO Tracking ทุกพฤหัสเช้า ส่งรายชื่อคนที่:
              <span className="font-semibold"> ผ่าน EM Begin ครบ 4 คลาส </span>
              หรือ <span className="font-semibold">เข้า MFinity (bt3) แล้ว</span>
              ไปยัง LINE ของเจ้าของเคส
            </div>
          </div>
        </div>

        <div className="flex items-center gap-2 flex-wrap">
          <Btn variant="ghost" size="sm" onClick={handlePreview} disabled={busy || !user?.id}
            icon={<Icon name="eye" className="w-3.5 h-3.5" />}>
            ดูข้อความตัวอย่าง
          </Btn>
          <Btn variant="brand" size="sm" onClick={handleSend} disabled={busy || !hasLine}
            icon={<Icon name="line" className="w-3.5 h-3.5" />}>
            {busy ? 'กำลังส่ง...' : 'ทดสอบส่ง LINE ตอนนี้'}
          </Btn>
          {!hasLine && (
            <span className="text-[11px] text-rose-600">กรุณาเชื่อม LINE ก่อนถึงจะทดสอบส่งได้</span>
          )}
        </div>

        {preview && (
          <pre className="text-[11px] leading-relaxed whitespace-pre-wrap bg-ink-900 text-purple-200 rounded-xl p-4 font-mono max-h-72 overflow-y-auto"
               style={{ fontFamily: 'ui-monospace, SFMono-Regular, Menlo, monospace' }}>
            {preview}
          </pre>
        )}

        {result && (
          <div className={`rounded-lg p-3 text-[11px] ${result.ok ? 'bg-emerald-50 border border-emerald-200 text-emerald-800' : 'bg-rose-50 border border-rose-200 text-rose-700'}`}>
            {result.ok ? (
              <>
                <div className="font-semibold">{result.mode === 'preview' ? '✓ ดึงข้อมูลเรียบร้อย' : '✓ ส่ง LINE เรียบร้อย'}</div>
                {result.results?.map((r, i) => (
                  <div key={i} className="mt-1 num">→ {r.name}: {r.count} คน {r.ok ? '✓' : `✗ ${r.error || ''}`}</div>
                ))}
              </>
            ) : (
              <>
                <div className="font-semibold">✗ ส่งไม่สำเร็จ</div>
                <div className="mt-1">{result.error}</div>
              </>
            )}
          </div>
        )}
      </div>
    </Card>
  );
}

function Toggle({ defaultOn }) {
  const [on, setOn] = useState(defaultOn);
  return (
    <button onClick={() => setOn(!on)}
      className={`relative w-10 h-6 rounded-full transition-colors flex-shrink-0 ${on ? 'bg-emerald-500' : 'bg-ink-200'}`}>
      <div className={`absolute top-0.5 w-5 h-5 rounded-full bg-white shadow-md transition-all ${on ? 'left-[18px]' : 'left-0.5'}`} />
    </button>
  );
}

function FieldLabel({ children }) {
  return <div className="text-[11px] font-medium text-ink-500 mb-1.5 uppercase tracking-wide">{children}</div>;
}

// Compute a light tint from a hex color (mix with white ~88%)
function colorTint(hex) {
  const h = hex.replace('#', '');
  const r = parseInt(h.slice(0,2), 16);
  const g = parseInt(h.slice(2,4), 16);
  const b = parseInt(h.slice(4,6), 16);
  const mix = (c) => Math.round(c + (255 - c) * 0.88);
  const toHex = (n) => n.toString(16).padStart(2, '0');
  return '#' + toHex(mix(r)) + toHex(mix(g)) + toHex(mix(b));
}

// ===================== Profile editor =====================
// QR-link modal — owner opens it, we create a token row, render a QR code
// pointing at /?partner_link=<token>, and poll until the partner claims it
// from their phone. The poll closes the modal and calls onComplete.
// Telegram deep-link modal. Creates a token row, opens
// https://t.me/<bot>?start=<token> in a new tab, and polls the row until
// the telegram-webhook edge function flips it to 'completed'.
function TelegramLinkModal({ target, ownerUserId, onClose, onComplete }) {
  const open = !!target; // 'self' | 'partner' | null
  const [phase, setPhase] = useState('init'); // init | waiting | done | expired | error
  const [token, setToken] = useState(null);
  const [expiresAt, setExpiresAt] = useState(null);
  const [err, setErr] = useState('');
  const [remaining, setRemaining] = useState(900);
  const botUsername = (window.EMPHASIS_ENV?.telegramBotUsername || '').replace(/^@/, '');
  const isConfigured = !!botUsername && !botUsername.startsWith('REPLACE');

  // Mint a token when the modal opens.
  useEffect(() => {
    if (!open || !ownerUserId) return;
    let cancelled = false;
    setPhase('init'); setErr(''); setToken(null);
    (async () => {
      try {
        if (!window.SB_READY) throw new Error('ระบบไม่ได้เชื่อมต่อฐานข้อมูล');
        if (!isConfigured) throw new Error('ยังไม่ได้ตั้ง telegramBotUsername ใน lib/config.jsx');
        const { data, error } = await window.supabase
          .from('telegram_link_tokens')
          .insert({ owner_user_id: ownerUserId, target })
          .select()
          .single();
        if (cancelled) return;
        if (error) throw error;
        setToken(data.token);
        setExpiresAt(data.expires_at);
        setPhase('waiting');
      } catch (e) {
        if (cancelled) return;
        setErr(e.message);
        setPhase('error');
      }
    })();
    return () => { cancelled = true; };
  }, [open, ownerUserId, target, isConfigured]);

  // Poll every 2.5s for the webhook to flip the row to completed.
  useEffect(() => {
    if (phase !== 'waiting' || !token) return;
    let cancelled = false;
    const id = setInterval(async () => {
      if (cancelled) return;
      const { data, error } = await window.supabase
        .from('telegram_link_tokens').select('*').eq('token', token).maybeSingle();
      if (cancelled || error || !data) return;
      if (data.status === 'completed') {
        clearInterval(id);
        setPhase('done');
        setTimeout(() => { if (!cancelled) onComplete?.(data); }, 800);
      } else if (new Date(data.expires_at).getTime() < Date.now()) {
        clearInterval(id);
        setPhase('expired');
      }
    }, 2500);
    return () => { cancelled = true; clearInterval(id); };
  }, [phase, token, onComplete]);

  // Countdown ticker.
  useEffect(() => {
    if (phase !== 'waiting' || !expiresAt) return;
    const tick = () => {
      const s = Math.max(0, Math.floor((new Date(expiresAt).getTime() - Date.now()) / 1000));
      setRemaining(s);
      if (s <= 0) setPhase('expired');
    };
    tick();
    const id = setInterval(tick, 1000);
    return () => clearInterval(id);
  }, [phase, expiresAt]);

  const deepLink = token && isConfigured ? `https://t.me/${botUsername}?start=${token}` : '';
  const mm = String(Math.floor(remaining / 60)).padStart(1, '0');
  const ss = String(remaining % 60).padStart(2, '0');

  return (
    <Modal open={open} onClose={onClose} size="md"
      title={
        <div className="flex items-center gap-2">
          <div className="w-7 h-7 rounded-md flex items-center justify-center text-white" style={{ background: '#229ED9' }}>
            <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor">
              <path d="M22 3 1 11l6 2 3 8 4-4 6 5z"/>
            </svg>
          </div>
          <div>
            <div className="text-sm font-semibold text-ink-900">เชื่อม Telegram {target === 'partner' ? '(คู่ร่วมธุรกิจ)' : ''}</div>
            <div className="text-[11px] text-ink-400">เปิดลิงก์แล้วกด "Start" ใน Telegram</div>
          </div>
        </div>
      }>
      <div className="space-y-4 text-center">
        {!isConfigured && (
          <div className="rounded-lg p-3 bg-amber-50 border border-amber-200 text-[11px] text-amber-800 text-left leading-relaxed">
            ⚠️ ยังไม่ได้ตั้งค่า Telegram bot — ใส่ <code className="bg-white px-1 rounded">telegramBotUsername</code> ใน <code className="bg-white px-1 rounded">lib/config.jsx</code> ก่อน
            แล้ว ตั้ง secret <code className="bg-white px-1 rounded">TELEGRAM_BOT_TOKEN</code> ใน Supabase
          </div>
        )}

        {phase === 'init' && isConfigured && (
          <div className="py-12 text-sm text-ink-500">
            <svg className="w-6 h-6 mx-auto mb-2 animate-spin text-sky-500" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5"><path d="M21 12a9 9 0 1 1-6.2-8.55" strokeLinecap="round" /></svg>
            กำลังสร้างลิงก์...
          </div>
        )}

        {phase === 'waiting' && (
          <>
            <ol className="text-left text-[12px] text-ink-700 space-y-1.5 leading-relaxed bg-ink-50/60 rounded-lg p-3">
              <li><b>1.</b> กดปุ่ม "เปิด Telegram" ด้านล่าง</li>
              <li><b>2.</b> ใน Telegram → กด <b>Start</b> (หรือ <b>เริ่ม</b>) ที่ bot</li>
              <li><b>3.</b> ระบบจะตอบกลับว่าเชื่อมเรียบร้อย</li>
            </ol>
            <a href={deepLink} target="_blank" rel="noopener noreferrer"
              className="block w-full h-11 rounded-lg text-white font-semibold inline-flex items-center justify-center gap-2"
              style={{ background: '#229ED9' }}>
              <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M22 3 1 11l6 2 3 8 4-4 6 5z"/></svg>
              เปิด Telegram
            </a>
            <div className="flex items-center justify-center gap-3 text-[11px]">
              <span className="inline-flex items-center gap-1.5 text-ink-500">
                <svg className="w-3 h-3 animate-spin text-sky-500" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5"><path d="M21 12a9 9 0 1 1-6.2-8.55" strokeLinecap="round" /></svg>
                รอ Telegram ตอบกลับ...
              </span>
              <span className="text-ink-300">·</span>
              <span className="text-ink-500 num">ลิงก์หมดอายุใน {mm}:{ss}</span>
            </div>
          </>
        )}

        {phase === 'done' && (
          <div className="py-10">
            <div className="w-14 h-14 rounded-full mx-auto mb-3 bg-emerald-500 text-white flex items-center justify-center">
              <Icon name="check" className="w-7 h-7" strokeWidth={3} />
            </div>
            <div className="text-base font-bold text-emerald-700">เชื่อม Telegram สำเร็จ 🎉</div>
            <div className="text-[11px] text-ink-500 mt-1">กำลังบันทึก...</div>
          </div>
        )}

        {phase === 'expired' && (
          <div className="py-8">
            <div className="text-sm font-semibold text-rose-700">ลิงก์หมดอายุ</div>
            <div className="text-[11px] text-ink-500 mt-1 mb-3">กรุณาสร้างลิงก์ใหม่</div>
            <Btn variant="brand" size="sm" onClick={() => { setPhase('init'); setToken(null); }}>สร้างลิงก์ใหม่</Btn>
          </div>
        )}

        {phase === 'error' && (
          <div className="py-8">
            <div className="text-sm font-semibold text-rose-700">สร้างลิงก์ไม่สำเร็จ</div>
            <div className="text-[11px] text-ink-500 mt-1 mb-3">{err}</div>
          </div>
        )}
      </div>
    </Modal>
  );
}

function PartnerLinkModal({ open, ownerUserId, onClose, onComplete }) {
  const [token, setToken] = useState(null);
  const [expiresAt, setExpiresAt] = useState(null);
  const [phase, setPhase] = useState('init'); // init | waiting | done | expired | error
  const [err, setErr] = useState('');
  const [remaining, setRemaining] = useState(600);

  // Mint a token whenever the modal opens.
  useEffect(() => {
    if (!open || !ownerUserId) return;
    let cancelled = false;
    setPhase('init'); setErr(''); setToken(null);
    (async () => {
      try {
        if (!window.SB_READY || !window.db?.partnerLink) {
          throw new Error('ระบบไม่ได้เชื่อมต่อฐานข้อมูล');
        }
        const { data, error } = await window.db.partnerLink.create(ownerUserId);
        if (cancelled) return;
        if (error) throw error;
        setToken(data.token);
        setExpiresAt(data.expires_at);
        setPhase('waiting');
      } catch (e) {
        if (cancelled) return;
        setErr(e.message);
        setPhase('error');
      }
    })();
    return () => { cancelled = true; };
  }, [open, ownerUserId]);

  // Poll the token status every 2.5s while waiting.
  useEffect(() => {
    if (phase !== 'waiting' || !token) return;
    let cancelled = false;
    const id = setInterval(async () => {
      if (cancelled) return;
      const { data, error } = await window.db.partnerLink.get(token);
      if (cancelled) return;
      if (error) return; // transient
      if (!data) return;
      if (data.status === 'completed') {
        clearInterval(id);
        setPhase('done');
        // Give the user a beat to see the success, then close.
        setTimeout(() => {
          if (!cancelled) {
            onComplete?.(data);
            onClose?.();
          }
        }, 1200);
      } else if (new Date(data.expires_at).getTime() < Date.now()) {
        clearInterval(id);
        setPhase('expired');
      }
    }, 2500);
    return () => { cancelled = true; clearInterval(id); };
  }, [phase, token]);

  // Countdown timer for the QR's freshness.
  useEffect(() => {
    if (phase !== 'waiting' || !expiresAt) return;
    const tick = () => {
      const s = Math.max(0, Math.floor((new Date(expiresAt).getTime() - Date.now()) / 1000));
      setRemaining(s);
      if (s <= 0) setPhase('expired');
    };
    tick();
    const id = setInterval(tick, 1000);
    return () => clearInterval(id);
  }, [phase, expiresAt]);

  // The URL the partner's phone scans.
  const linkUrl = token ? `${window.location.origin}/?partner_link=${token}` : '';
  // QR via a public image API — no library dependency. ECC=M, 320×320.
  const qrSrc = linkUrl
    ? `https://api.qrserver.com/v1/create-qr-code/?size=320x320&ecc=M&data=${encodeURIComponent(linkUrl)}`
    : null;

  const mm = String(Math.floor(remaining / 60)).padStart(1, '0');
  const ss = String(remaining % 60).padStart(2, '0');

  return (
    <Modal open={open} onClose={onClose} size="md"
      title={
        <div className="flex items-center gap-2">
          <div className="w-7 h-7 rounded-md flex items-center justify-center text-white" style={{ background: '#06C755' }}>
            <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor">
              <path d="M19 4H5a2 2 0 0 0-2 2v9c0 2.5 2.2 4.5 5 5l-.8 2.2a.5.5 0 0 0 .8.5l4-3.7H19a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2z" />
            </svg>
          </div>
          <div>
            <div className="text-sm font-semibold text-ink-900">เชื่อม LINE คู่ร่วมธุรกิจ</div>
            <div className="text-[11px] text-ink-400">สแกน QR ด้วยมือถือคู่ร่วมธุรกิจ</div>
          </div>
        </div>
      }>
      <div className="text-center space-y-4">
        {phase === 'init' && (
          <div className="py-12 text-sm text-ink-500">
            <svg className="w-6 h-6 mx-auto mb-2 animate-spin text-brand-500" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5"><path d="M21 12a9 9 0 1 1-6.2-8.55" strokeLinecap="round" /></svg>
            กำลังสร้าง QR...
          </div>
        )}

        {phase === 'waiting' && (
          <>
            <div className="inline-block p-3 bg-white rounded-2xl border-2 border-emerald-200 shadow-card">
              {qrSrc ? (
                <img src={qrSrc} alt="QR partner link" className="w-64 h-64" />
              ) : (
                <div className="w-64 h-64 bg-ink-50 rounded-lg" />
              )}
            </div>
            <ol className="text-left text-[12px] text-ink-700 space-y-1.5 leading-relaxed bg-ink-50/60 rounded-lg p-3">
              <li><b>1.</b> เปิดกล้อง / LINE บนมือถือคู่ร่วมธุรกิจ</li>
              <li><b>2.</b> สแกน QR Code นี้</li>
              <li><b>3.</b> Login LINE บนมือถือ → ระบบจะเชื่อมข้อมูลให้อัตโนมัติ</li>
            </ol>
            <div className="flex items-center justify-center gap-3 text-[11px]">
              <span className="inline-flex items-center gap-1.5 text-ink-500">
                <svg className="w-3 h-3 animate-spin text-emerald-500" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5"><path d="M21 12a9 9 0 1 1-6.2-8.55" strokeLinecap="round" /></svg>
                รอการสแกน...
              </span>
              <span className="text-ink-300">·</span>
              <span className="text-ink-500 num">QR หมดอายุใน {mm}:{ss}</span>
            </div>
          </>
        )}

        {phase === 'done' && (
          <div className="py-10">
            <div className="w-14 h-14 rounded-full mx-auto mb-3 bg-emerald-500 text-white flex items-center justify-center">
              <Icon name="check" className="w-7 h-7" strokeWidth={3} />
            </div>
            <div className="text-base font-bold text-emerald-700">เชื่อม LINE สำเร็จ 🎉</div>
            <div className="text-[11px] text-ink-500 mt-1">กำลังบันทึก...</div>
          </div>
        )}

        {phase === 'expired' && (
          <div className="py-8">
            <div className="text-sm font-semibold text-rose-700">QR หมดอายุแล้ว</div>
            <div className="text-[11px] text-ink-500 mt-1 mb-3">กรุณาสร้าง QR ใหม่</div>
            <Btn variant="brand" size="sm" onClick={() => { setPhase('init'); setToken(null); }}>
              สร้าง QR ใหม่
            </Btn>
          </div>
        )}

        {phase === 'error' && (
          <div className="py-8">
            <div className="text-sm font-semibold text-rose-700">สร้าง QR ไม่สำเร็จ</div>
            <div className="text-[11px] text-ink-500 mt-1 mb-3">{err}</div>
            <Btn variant="brand" size="sm" onClick={() => { setPhase('init'); setToken(null); }}>ลองใหม่</Btn>
          </div>
        )}
      </div>
    </Modal>
  );
}

