/* PayBO v1 — Transaction drawer with state machine visualizer, v2 Risk
   placeholder, scoped activity log, and action modal (reason + 10-char
   note + partial approval). Replaces the earlier lightweight drawer. */

const ACTION_LABEL = {
  CREATED: "Created",
  AUTO_APPROVED: "Auto-approved",
  FLAGGED_TO_CONFIRM: "Flagged · To Confirm",
  DECLINED: "Declined (limits)",
  OPERATOR_APPROVED: "Operator approved",
  OPERATOR_REJECTED: "Operator rejected",
  OPERATOR_PARTIAL_APPROVED: "Operator partial-approved",
  PARTIAL_REJECT: "Partial reject (remainder)",
  SENT_TO_PROVIDER: "Sent to provider",
  PROVIDER_ACCEPTED: "Provider accepted",
  PROVIDER_REJECTED: "Provider rejected",
  BALANCED: "Balanced",
  FAILED: "Failed",
};

const actorDot = (a) =>
  a === "OPERATOR" ? "var(--primary, #1e40af)"
  : a === "PROVIDER" ? "#7c3aed"
  : "#6b7280";

const StateMachine = ({ tx }) => {
  const { STATE_PILLS, ALT_PILLS } = window.PAYBO;
  const st = tx.status;
  const altKey = st === "declined" ? "declined" : st === "rejected" ? "rejected" : st === "failed" ? "failed" : null;
  const mainIdx = STATE_PILLS.findIndex(p => p.key === st);
  return (
    <div className="paybo-fsm">
      {STATE_PILLS.map((p, i) => {
        const cls =
          p.key === st ? "current"
          : (altKey ? (i === 0 ? "done" : "") : (i < mainIdx ? "done" : ""));
        return (
          <React.Fragment key={p.key}>
            {i > 0 && <span className={`paybo-fsm__arrow ${cls === "done" || cls === "current" ? "done" : ""}`}/>}
            <span className={`paybo-fsm__pill ${cls}`}>{p.label}</span>
          </React.Fragment>
        );
      })}
      {altKey && (
        <>
          <span className="paybo-fsm__arrow"/>
          <span className={`paybo-fsm__pill ${altKey === "failed" ? "terminal-err" : altKey === "declined" ? "terminal-err" : "terminal-err"}`}>
            {ALT_PILLS[altKey].label}
          </span>
        </>
      )}
    </div>
  );
};

const ActionModal = ({ tx, action, onCancel, onConfirm }) => {
  // Free-text note is the single source of decision rationale — no
  // predefined reason picker. Min 10 chars enforced before Confirm.
  const [note, setNote] = useState("");
  const [partialAmt, setPartialAmt] = useState("");
  const noteOk = note.trim().length >= 10;
  let partialOk = true;
  if (action === "partial") {
    const n = parseFloat(partialAmt);
    partialOk = !isNaN(n) && n > 0 && n < tx.amount;
  }
  const canConfirm = noteOk && partialOk;

  const header =
    action === "approve" ? "Approve transaction" :
    action === "reject"  ? "Reject transaction"  :
                           "Partial approval";
  const confirmClass =
    action === "reject" ? "paybo-btn paybo-btn--danger"
                        : "paybo-btn paybo-btn--primary";

  return (
    <div className="paybo-modal-bg" onClick={onCancel}>
      <div className="paybo-modal" onClick={e => e.stopPropagation()}>
        <div className="paybo-modal__head">
          <div style={{width:34, height:34, borderRadius:8,
            background: action==="reject" ? "var(--err-bg, #fee2e2)"
                      : action==="partial" ? "var(--warn-bg, #fef3c7)"
                      : "var(--ok-bg, #d1fae5)",
            color: action==="reject" ? "var(--err)"
                 : action==="partial" ? "var(--warn)"
                 : "var(--ok)",
            display:"grid", placeItems:"center"}}>
            <Icon name={action==="reject" ? "x" : action==="partial" ? "flag" : "check"} size={15}/>
          </div>
          <div style={{flex:1}}>
            <div className="paybo-modal__title">{header}</div>
            <div className="paybo-modal__sub">
              {tx.id} · <Money amount={tx.amount} currency={tx.currency}/> · {tx.user_name}
            </div>
          </div>
          <button className="paybo-btn paybo-btn--ghost" onClick={onCancel}>
            <Icon name="x" size={13}/>
          </button>
        </div>

        <div className="paybo-modal__body">
          {window.Explainer && (
            <Explainer compact title="What this decision does">
              {action === "approve" && <>Releases the transaction from the To-Confirm queue at its <strong>full amount</strong>. It is then sent to the PSP through the route's chain. PSP decides whether it ends in <strong>Balanced</strong> (settled) or <strong>Failed</strong>. Counts toward the Approval rate.</>}
              {action === "reject" && <>Refuses the transaction. It is <strong>not</strong> sent to any PSP — terminal state. Counts toward the denominator of Approval rate (lowers the rate). Use for failed KYC, hard fraud signals, manually-blocked methods, or player-requested cancellations.</>}
              {action === "partial" && <>Releases the transaction for a <strong>smaller amount</strong> than the player requested. The approved slice is sent to the PSP exactly like a full approval; the remainder is logged as <code>PARTIAL_REJECT</code> in the Activity log so the rejected portion stays auditable.</>}
            </Explainer>
          )}

          {action === "partial" && (
            <div className="paybo-field">
              <label className="req">
                Approved amount
                {window.Tip && <Tip>The portion of the original amount you are releasing for processing. Must be greater than 0 and less than the original amount ({tx.amount} {tx.currency}). The remainder is automatically logged as a partial reject.</Tip>}
              </label>
              <input type="number" min="0.01" max={tx.amount - 0.01} step="0.01"
                placeholder={`< ${tx.amount}`}
                value={partialAmt} onChange={e => setPartialAmt(e.target.value)}/>
              <div style={{fontSize:11, color:"#6b7280"}}>
                Must be &gt; 0 and &lt; {tx.amount} {tx.currency}. Remainder will be logged as <code>PARTIAL_REJECT</code>.
              </div>
            </div>
          )}

          <div className="paybo-field">
            <label className="req">
              Note (minimum 10 characters)
              {window.Tip && <Tip>Free-text rationale for your decision. Captured immutably in the Activity log with your operator ID and the timestamp. Used by player support if the player calls in about this transaction — write something a colleague can read in 30 seconds and understand the call.</Tip>}
            </label>
            <textarea value={note} onChange={e => setNote(e.target.value)}
              placeholder="Briefly explain your decision — what was verified, who confirmed, why…"/>
            <div className={`paybo-field__counter ${noteOk ? "paybo-field__counter--ok" : "paybo-field__counter--err"}`}>
              {note.trim().length}/10 {noteOk ? "✓" : "characters required"}
            </div>
          </div>

          <div style={{fontSize:11.5, color:"#6b7280", lineHeight:1.6, background:"#f4f5f8", padding:"10px 12px", borderRadius:6}}>
            This action is <strong>immutable</strong>. It will be written to the activity log with your operator ID, a timestamp, and the full note text. It cannot be edited or deleted.
          </div>
        </div>

        <div className="paybo-modal__foot">
          <button className="paybo-btn paybo-btn--ghost" onClick={onCancel}>Cancel</button>
          <button className={confirmClass} disabled={!canConfirm}
            onClick={() => onConfirm({ note, approved_amount: action==="partial" ? parseFloat(partialAmt) : null })}>
            {action === "reject" ? "Reject"
              : action === "partial" ? "Partial approve"
              : "Approve"}
          </button>
        </div>
      </div>
    </div>
  );
};

const TxDetail = ({ tx, onClose, onStatusChange }) => {
  const [action, setAction] = useState(null);
  const [copied, setCopied] = useState(false);
  if (!tx) return null;

  const copyId = () => {
    /* try/catch does not catch a REJECTED promise, so the old version flashed
       "Copied" even when the clipboard write was refused. Mirror the fallback
       CopyableId in ui.jsx already uses, and only confirm on actual success. */
    const done = () => { setCopied(true); setTimeout(() => setCopied(false), 1400); };
    const fallback = () => {
      const ta = document.createElement("textarea");
      ta.value = String(tx.id); ta.setAttribute("readonly", "");
      ta.style.position = "fixed"; ta.style.opacity = "0";
      document.body.appendChild(ta); ta.select();
      try { if (document.execCommand("copy")) done(); } finally { document.body.removeChild(ta); }
    };
    try {
      if (navigator.clipboard?.writeText) navigator.clipboard.writeText(String(tx.id)).then(done).catch(fallback);
      else fallback();
    } catch (e) { fallback(); }
  };

  const isToConfirm = tx.status === "to_confirm";
  const activity = tx.activity || [];

  const applyAction = (payload) => {
    const { ACTION } = window.PAYBO;
    let newStatus, actionKey;
    if (action === "approve") { newStatus = "approved"; actionKey = ACTION.OPERATOR_APPROVED; }
    else if (action === "reject") { newStatus = "rejected"; actionKey = ACTION.OPERATOR_REJECTED; }
    else if (action === "partial") { newStatus = "approved"; actionKey = ACTION.OPERATOR_PARTIAL_APPROVED; }
    const before = tx.status;
    tx.status = newStatus;
    tx.updated_at = Date.now();
    if (action === "partial") tx.approved_amount = payload.approved_amount;

    const entry = window.PAYBO.logEntry({
      transaction_id: tx.id, actor_type: "OPERATOR", actor_id: "op1",
      action: actionKey, note: payload.note,
      before_state: before, after_state: newStatus,
      metadata: action === "partial"
        ? { amount: tx.amount, approved_amount: payload.approved_amount, remainder: +(tx.amount - payload.approved_amount).toFixed(2) }
        : { amount: tx.amount },
    });
    tx.activity = [...activity, entry];

    if (action === "partial") {
      const rem = window.PAYBO.logEntry({
        transaction_id: tx.id, actor_type: "SYSTEM",
        action: window.PAYBO.ACTION.PARTIAL_REJECT,
        before_state: before, after_state: newStatus,
        metadata: { remainder: +(tx.amount - payload.approved_amount).toFixed(2) },
      });
      tx.activity = [...tx.activity, rem];
    }
    setAction(null);
    if (onStatusChange) onStatusChange(tx);
  };

  return (
    <div style={{position:"fixed", inset:0, background:"rgba(15,20,32,.3)", zIndex:90}} onClick={onClose}>
      <div className="drawer" onClick={e => e.stopPropagation()}>
        <div className="drawer__head">
          <div>
            <div style={{fontSize:11, color:"var(--text-tertiary)", textTransform:"uppercase", letterSpacing:".05em", fontWeight:600}}>
              Transaction
            </div>
            <div style={{display:"flex", alignItems:"center", gap:8}}>
              <div style={{fontSize:15, fontWeight:600}} className="mono">{tx.id}</div>
              <button className="btn btn--ghost btn--icon btn--sm" title="Copy ID" onClick={copyId}>
                <Icon name={copied ? "check" : "copy"} size={12}/>
              </button>
            </div>
          </div>
          <div style={{marginLeft:"auto", display:"flex", gap:6}}>
            <StatusChip status={tx.status}/>
            <button className="btn btn--ghost btn--icon btn--sm" onClick={onClose}>
              <Icon name="x" size={13}/>
            </button>
          </div>
        </div>

        <div className="drawer__body">
          <div style={{display:"flex", alignItems:"baseline", gap:10, marginBottom:10}}>
            <div style={{fontSize:32, fontWeight:650, letterSpacing:"-0.02em", color:"var(--paybo-heading, #1e3a8a)"}}>
              <Money amount={tx.amount} currency={tx.currency}/>
            </div>
            <TypeChip type={tx.type}/>
            {tx.approved_amount != null && (
              <span style={{fontSize:11, padding:"3px 7px", borderRadius:6, background:"var(--warn-bg, #fef3c7)", color:"var(--warn, #d97706)", fontWeight:600}}>
                Approved <Money amount={tx.approved_amount} currency={tx.currency}/>
              </span>
            )}
          </div>

          <StateMachine tx={tx}/>

          <div style={{fontSize:11, fontWeight:600, textTransform:"uppercase", letterSpacing:".05em", color:"var(--text-tertiary)", marginBottom:8}}>Details</div>
          <div style={{display:"grid", gridTemplateColumns:"150px 1fr", gap:"8px 14px", fontSize:13, marginBottom:16}}>
            <div className="dim">Created</div><div>{formatTs(tx.created_at)}</div>
            <div className="dim">Player</div><div><a style={{color:"var(--primary, #1e40af)", cursor:"pointer", textDecoration:"underline"}}>{tx.user_name} · {tx.user_id}</a></div>
            <div className="dim">Brand</div><div>
              <span style={{display:"inline-flex", alignItems:"center", gap:6}}>
                <span style={{width:14, height:14, borderRadius:4, background: tx.brand_color, color:"#fff", fontSize:8, fontWeight:700, display:"grid", placeItems:"center"}}>{tx.brand_short}</span>
                {tx.brand_name}
              </span>
            </div>
            <div className="dim">Method</div><div>{tx.method_name}</div>
            <div className="dim">Currency</div><div>{tx.currency}</div>
            <div className="dim">Fee</div><div><Money amount={tx.fee} currency={tx.currency}/></div>
            <div className="dim">Net amount</div><div className="strong"><Money amount={tx.net_amount ?? (tx.amount - tx.fee)} currency={tx.currency}/></div>
            <div className="dim">Provider ref</div><div className="mono">{tx.provider_ref || "—"}</div>
            <div className="dim">IP address</div><div className="mono">{tx.ip_address || "—"}</div>
            <div className="dim">Reason code</div><div className="mono">{tx.reason_code || "—"}</div>
            <div className="dim">Threshold</div><div>
              <Money amount={tx.threshold ?? window.PAYBO.getGlobalThreshold(tx.method, tx.currency)} currency={tx.currency}/>
              <span style={{marginLeft:6, fontSize:11, color:"var(--text-tertiary)"}}>(global · {tx.method_name})</span>
            </div>
          </div>

          {/* Scoped activity log */}
          <div style={{fontSize:11, fontWeight:600, textTransform:"uppercase", letterSpacing:".05em", color:"var(--text-tertiary)", marginBottom:8}}>Activity log</div>
          <div style={{display:"flex", flexDirection:"column", gap:10, paddingLeft:12, borderLeft:"2px solid var(--border-default, #e5e7eb)", marginBottom:16}}>
            {activity.map((a, i) => (
              <div key={a.id || i} style={{position:"relative", paddingLeft:14}}>
                <div style={{position:"absolute", left:-17, top:4, width:10, height:10, borderRadius:999, background: actorDot(a.actor_type), border:"2px solid var(--n-0, #fff)"}}/>
                <div style={{fontSize:12.5, fontWeight:550}}>
                  {ACTION_LABEL[a.action] || a.action}
                  <span style={{fontWeight:400, color:"var(--text-tertiary)", marginLeft:6}}>
                    {new Date(a.timestamp).toLocaleTimeString([], { hour:"2-digit", minute:"2-digit" })}
                  </span>
                  <span style={{marginLeft:6, fontSize:10, padding:"1px 5px", borderRadius:999, background:"#f4f5f8", color:"#475569", fontWeight:600, textTransform:"uppercase", letterSpacing:"0.05em"}}>
                    {a.actor_type}
                  </span>
                </div>
                {(a.reason_code || a.note) && (
                  <div style={{fontSize:11.5, color:"var(--text-secondary, #475569)", marginTop:2}}>
                    {a.reason_code && <span className="mono" style={{marginRight:8}}>{a.reason_code}</span>}
                    {a.note && <span>· {a.note}</span>}
                  </div>
                )}
                {a.before_state && a.after_state && (
                  <div style={{fontSize:11, color:"var(--text-tertiary, #6b7280)", marginTop:2}}>
                    {a.before_state} → <strong>{a.after_state}</strong>
                  </div>
                )}
              </div>
            ))}
          </div>

          {isToConfirm && (
            <div style={{display:"flex", gap:8, paddingTop:10, borderTop:"1px solid var(--border-default, #e5e7eb)"}}>
              <button className="paybo-btn paybo-btn--primary" style={{flex:1}} onClick={() => setAction("approve")}
                title="Release the transaction at the full amount and send it to the PSP through the route's chain.">
                <Icon name="check" size={13}/> Approve
              </button>
              <button className="paybo-btn paybo-btn--secondary" style={{flex:1}} onClick={() => setAction("partial")}
                title="Release a smaller portion of the transaction; the remainder is logged as a partial reject.">
                <Icon name="flag" size={13}/> Partial approve
              </button>
              <button className="paybo-btn paybo-btn--danger" style={{flex:1}} onClick={() => setAction("reject")}
                title="Refuse the transaction. It is not sent to any PSP — terminal state. Operator note required.">
                <Icon name="x" size={13}/> Reject
              </button>
            </div>
          )}
          {!isToConfirm && (
            <div style={{padding:"10px 12px", fontSize:12, color:"var(--text-tertiary)", background:"#f4f5f8", borderRadius:6, textAlign:"center"}}>
              Read-only · this transaction is {tx.status === "balanced" || tx.status === "approved" || tx.status === "pending" || tx.status === "created" ? "not" : "no longer"} in the review queue.
            </div>
          )}
        </div>
      </div>

      {action && <ActionModal tx={tx} action={action} onCancel={() => setAction(null)} onConfirm={applyAction}/>}
    </div>
  );
};
