/**
 * Monaco-based code strategy editor.
 * Users write a JS strategy function; "Run Backtest" posts it to /api/backtest.
 */
import { Suspense, lazy, useCallback, useEffect, useRef, useState } from 'react';
import { Play, Save, Copy, RotateCcw } from 'lucide-react';
import { getPlatformAccessToken } from '../../lib/authSession';
import { createTradeProfile } from '../../lib/profileApi';
import { ensureMonacoLoaderConfigured } from '../../lib/monacoLocalWorkers';
import { tradingRuntime } from '../../lib/runtime';
import { createRequestId } from '../../../../shared/request-id.js';
import { Button } from '../ui/button';
import { Card, CardContent } from '../ui/card';

const DEFAULT_TEMPLATE = `/**
 * Custom Trading Strategy
 *
 * Available context:
 *   symbol    – ticker being traded (string)
 *   price     – latest close price (number)
 *   rsi       – RSI(14) value (number)
 *   ema50     – EMA 50 (number)
 *   ema200    – EMA 200 (number)
 *   macd      – MACD signal (number)
 *   volume    – current volume (number)
 *
 * Return one of:
 *   { signal: 'BUY',  quantity: 10 }
 *   { signal: 'SELL', quantity: 10 }
 *   { signal: 'HOLD' }
 */
function strategy({ symbol, price, rsi, ema50, ema200, macd, volume }) {
  // Example: RSI oversold + price above EMA200 → BUY
  if (rsi < 30 && price > ema200) {
    return { signal: 'BUY', quantity: 10 };
  }
  // Example: RSI overbought → SELL
  if (rsi > 70) {
    return { signal: 'SELL', quantity: 10 };
  }
  return { signal: 'HOLD' };
}
`;

const MonacoEditor = lazy(() => import('@monaco-editor/react'));

interface BacktestResult {
  trades?:        number;
  winRate?:       number;
  totalReturn?:   number;
  sharpeRatio?:   number;
  maxDrawdown?:   number;
  [key: string]:  any;
}

interface Props {
  symbol: string;
  allocatedCapital?: number;
  riskPerTradePercent?: number;
}

export function CodeStrategyEditor({
  symbol,
  allocatedCapital = 1000,
  riskPerTradePercent = 1,
}: Props) {
  const [code,     setCode]     = useState(DEFAULT_TEMPLATE);
  const [running,  setRunning]  = useState(false);
  const [saving,   setSaving]   = useState(false);
  const [result,   setResult]   = useState<BacktestResult | null>(null);
  const [error,    setError]    = useState<string | null>(null);
  const [saved,    setSaved]    = useState(false);
  const mountedRef = useRef(true);
  const savedResetTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);

  const clearSavedResetTimeout = useCallback(() => {
    if (savedResetTimeoutRef.current) {
      clearTimeout(savedResetTimeoutRef.current);
      savedResetTimeoutRef.current = null;
    }
  }, []);

  useEffect(() => {
    mountedRef.current = true;
    void ensureMonacoLoaderConfigured();
    return () => {
      mountedRef.current = false;
      clearSavedResetTimeout();
    };
  }, [clearSavedResetTimeout]);

  const handleRunBacktest = async () => {
    setRunning(true);
    setError(null);
    setResult(null);
    try {
      const token = await getPlatformAccessToken();
      // Backend `/api/backtest/run` expects `{ symbols: string[], strategyConfig: object }`.
      // We wrap the user's JS in a strategyConfig of type `code` so the engine
      // can route to a sandboxed evaluator (see backend custom-strategy handler).
      const res = await fetch(`${tradingRuntime.tradingApiUrl}/api/backtest/run`, {
        method: 'POST',
        headers: {
          'Content-Type':  'application/json',
          Authorization:   `Bearer ${token}`,
          'x-request-id':  createRequestId('web-backtest'),
        },
        body: JSON.stringify({
          symbols: [symbol],
          strategyConfig: {
            type: 'code',
            language: 'javascript',
            code,
          },
        }),
      });
      const data = await res.json().catch(() => ({})) as any;
      if (!res.ok || data?.success === false) {
        throw new Error(data?.error ?? `Backtest failed (${res.status})`);
      }
      // Backend may wrap results in { success, results } or return them flat.
      if (mountedRef.current) {
        setResult(data?.results ?? data);
      }
    } catch (err: any) {
      if (mountedRef.current) {
        setError(err?.message ?? 'Backtest failed');
      }
    } finally {
      if (mountedRef.current) {
        setRunning(false);
      }
    }
  };

  const handleSave = async () => {
    setSaving(true);
    setError(null);
    try {
      await createTradeProfile({
        name: `Code Strategy (${symbol})`,
        symbols: symbol,
        allocated_capital: allocatedCapital,
        risk_per_trade_percent: riskPerTradePercent,
        is_active: false,
        strategy_config: {
          type: 'code',
          version: 1,
          language: 'javascript',
          code,
        },
      });
      if (!mountedRef.current) return;
      setSaved(true);
      clearSavedResetTimeout();
      savedResetTimeoutRef.current = setTimeout(() => {
        if (mountedRef.current) {
          setSaved(false);
        }
        savedResetTimeoutRef.current = null;
      }, 3000);
    } catch (err: any) {
      if (mountedRef.current) {
        setSaved(false);
        setError(err?.message ?? 'Failed to save strategy');
      }
    } finally {
      if (mountedRef.current) {
        setSaving(false);
      }
    }
  };

  const handleReset = () => {
    setCode(DEFAULT_TEMPLATE);
    setResult(null);
    setError(null);
  };

  const handleCopy = () => {
    navigator.clipboard.writeText(code).catch(() => {});
  };

  const handleShortcut = (event: React.KeyboardEvent<HTMLDivElement>) => {
    if (!(event.metaKey || event.ctrlKey)) return;
    const key = event.key.toLowerCase();

    if (key === 's') {
      event.preventDefault();
      if (!saving) void handleSave();
    } else if (event.key === 'Enter' && !running) {
      event.preventDefault();
      void handleRunBacktest();
    }
  };

  const fmt = (n: number | undefined, suffix = '') =>
    n != null ? `${n.toFixed(2)}${suffix}` : '—';

  return (
    <div onKeyDownCapture={handleShortcut}>
      {/* Toolbar */}
      <div className="mb-3 flex items-center gap-2">
        <span className="text-sm font-semibold text-[var(--foreground)]">
          Code Editor — {symbol}
        </span>
        <span className="text-xs text-[var(--muted-foreground)]">
          Cmd/Ctrl-S save · Cmd/Ctrl-Enter backtest
        </span>
        <div style={{ flex: 1 }} />
        <Button variant="outline" size="sm" onClick={handleCopy} title="Copy code">
          <Copy size={13} /> Copy
        </Button>
        <Button variant="outline" size="sm" onClick={handleReset} title="Reset to template">
          <RotateCcw size={13} /> Reset
        </Button>
        <Button variant={saved ? 'outline' : 'default'} size="sm" onClick={handleSave} disabled={saving} title="Save code strategy (Cmd/Ctrl+S)">
          <Save size={13} /> {saving ? 'Saving...' : saved ? 'Saved!' : 'Save'}
        </Button>
        <Button size="sm" onClick={handleRunBacktest} disabled={running} title="Run backtest (Cmd/Ctrl+Enter)">
          <Play size={13} /> {running ? 'Running…' : 'Run Backtest'}
        </Button>
      </div>

      {/* Monaco editor */}
      <div style={{ border: '1px solid var(--border)', borderRadius: 16, overflow: 'hidden', background: 'var(--card)' }}>
        <Suspense fallback={<CodeEditorFallback />}>
          <MonacoEditor
            height="380px"
            defaultLanguage="javascript"
            value={code}
            onChange={v => {
              setCode(v ?? '');
              setSaved(false);
            }}
            theme={document.documentElement.classList.contains('dark') ? 'vs-dark' : 'light'}
            options={{
              fontSize:         13,
              minimap:          { enabled: false },
              scrollBeyondLastLine: false,
              wordWrap:         'on',
              lineNumbers:      'on',
              renderLineHighlight: 'gutter',
              padding:          { top: 12, bottom: 12 },
              fontFamily:       '"Fira Code", "Cascadia Code", "Consolas", monospace',
              fontLigatures:    true,
              tabSize:          2,
              automaticLayout:  true,
            }}
          />
        </Suspense>
      </div>

      {/* Error */}
      {error && (
        <Card className="mt-3 border-[var(--destructive)]/30 bg-[var(--destructive)]/10">
          <CardContent className="px-4 py-3 text-sm font-medium text-[var(--destructive)] [font-family:monospace]">
            {error}
          </CardContent>
        </Card>
      )}

      {/* Backtest results */}
      {result && (
        <Card className="mt-3 border-[var(--primary)]/20 bg-[var(--accent-soft)]">
          <CardContent className="space-y-3 px-4 py-4">
          <div style={{ fontSize: 13, fontWeight: 700, color: 'var(--primary)', marginBottom: 10 }}>
            Backtest Results
          </div>
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(5,1fr)', gap: 12 }}>
            {[
              ['Total Return',  fmt(result.totalReturn, '%')],
              ['Win Rate',      fmt(result.winRate, '%')],
              ['# Trades',      result.trades?.toString() ?? '—'],
              ['Sharpe Ratio',  fmt(result.sharpeRatio)],
              ['Max Drawdown',  fmt(result.maxDrawdown, '%')],
            ].map(([label, val]) => (
              <div key={label} style={{
                background: 'var(--card)', borderRadius: 12, padding: '10px 12px',
                border: '1px solid var(--border)',
              }}>
                <div style={{ fontSize: 10, color: 'var(--muted-foreground)', fontWeight: 500, marginBottom: 3 }}>
                  {label}
                </div>
                <div style={{ fontSize: 16, fontWeight: 700, color: 'var(--foreground)' }}>{val}</div>
              </div>
            ))}
          </div>

          {/* Trades table (if provided) */}
          {Array.isArray(result.tradeLog) && result.tradeLog.length > 0 && (
            <div style={{ marginTop: 12 }}>
              <div style={{ fontSize: 11, fontWeight: 600, color: '#374151', marginBottom: 6 }}>
                Trade Log (last 10)
              </div>
              <table style={{ width: '100%', borderCollapse: 'collapse', fontSize: 11 }}>
                <thead>
                  <tr style={{ borderBottom: '1px solid var(--border)' }}>
                    {['Date','Side','Price','Qty','P&L'].map(h => (
                      <th key={h} style={{ padding: '4px 8px', textAlign: 'left', color: 'var(--muted-foreground)', fontWeight: 600 }}>{h}</th>
                    ))}
                  </tr>
                </thead>
                <tbody>
                  {result.tradeLog.slice(-10).map((t: any, i: number) => (
                    <tr key={i} style={{ borderBottom: '1px solid var(--border)' }}>
                      <td style={{ padding: '4px 8px', color: 'var(--foreground)' }}>{t.date ?? '—'}</td>
                      <td style={{ padding: '4px 8px', color: t.side === 'BUY' ? '#16A34A' : '#DC2626', fontWeight: 600 }}>{t.side}</td>
                      <td style={{ padding: '4px 8px', color: 'var(--foreground)' }}>{t.price != null ? `$${t.price.toFixed(2)}` : '—'}</td>
                      <td style={{ padding: '4px 8px', color: 'var(--foreground)' }}>{t.qty ?? '—'}</td>
                      <td style={{ padding: '4px 8px', color: t.pnl >= 0 ? '#16A34A' : '#DC2626', fontWeight: 600 }}>
                        {t.pnl != null ? `${t.pnl >= 0 ? '+' : ''}$${t.pnl.toFixed(2)}` : '—'}
                      </td>
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>
          )}
          </CardContent>
        </Card>
      )}
    </div>
  );
}

function CodeEditorFallback() {
  return (
    <div
      role="status"
      aria-label="Loading code editor"
      style={{
        height: 380,
        display: 'flex',
        alignItems: 'center',
        justifyContent: 'center',
        background: 'var(--hero-gradient)',
        color: 'var(--muted-foreground)',
        fontSize: 13,
        fontWeight: 700,
      }}
    >
      Loading code editor…
    </div>
  );
}
