import React, {type CSSProperties, type ReactNode} from "react";
import {useCurrentFrame, useVideoConfig} from "remotion";
import {connectorGeometry, phase, positive, reveal, type MotionTiming, type Point} from "./core.js";

export type AppFrameProps = {children: ReactNode; title?: string; width?: number; height?: number; background?: string; style?: CSSProperties};
export function AppFrame({children, title = "Your product", width = 960, height = 540, background = "#fff", style}: AppFrameProps) {
  positive(width, "width"); positive(height, "height");
  return <div style={{width, height, display: "flex", flexDirection: "column", overflow: "hidden", border: "1px solid #d8d8d8", borderRadius: 18, background, boxShadow: "0 24px 72px #00000012", ...style}}>
    <div style={{height: 52, flex: "0 0 52px", display: "flex", alignItems: "center", gap: 8, padding: "0 20px", borderBottom: "1px solid #e9e9e9", background: "#fafafa"}}>
      <div aria-hidden style={{display: "flex", gap: 6}}>{[0, 1, 2].map(i => <span key={i} style={{width: 9, height: 9, borderRadius: "50%", background: "#d0d0d0"}} />)}</div>
      <span style={{fontSize: 16, color: "#666", marginLeft: 12}}>{title}</span>
    </div>
    <div style={{position: "relative", flex: 1, minHeight: 0, overflow: "hidden"}}>{children}</div>
  </div>;
}

export type ProcessNodeProps = {title: string; rows?: readonly string[]; state?: "idle" | "running" | "success" | "error"; accent?: string; width?: number; style?: CSSProperties};
export function ProcessNode({title, rows = [], state = "idle", accent = "#62972d", width = 300, style}: ProcessNodeProps) {
  positive(width, "width");
  if (!["idle", "running", "success", "error"].includes(state)) throw new TypeError("state must be idle, running, success, or error.");
  const active = state !== "idle", color = state === "error" ? "#b84636" : state === "idle" ? "#8b8b8b" : accent;
  return <div style={{width, boxSizing: "border-box", border: `1.5px solid ${active ? color : "#ddd"}`, borderRadius: 16, padding: 24, background: "#fff", color: "#171717", boxShadow: active ? `0 0 0 4px color-mix(in srgb, ${color} 10%, transparent)` : "none", ...style}}>
    <div style={{display: "flex", alignItems: "center", gap: 12, fontWeight: 650, fontSize: 24}}><span aria-label={state} style={{width: 10, height: 10, flexShrink: 0, borderRadius: "50%", background: color}} />{title}</div>
    {rows.length > 0 && <div style={{display: "grid", gap: 10, marginTop: 18, color: "#666", fontSize: 18}}>{rows.map((row, i) => <div key={i}>{row}</div>)}</div>}
  </div>;
}

export type ConnectorProps = MotionTiming & {from: Point; to: Point; width?: number; height?: number; bend?: number; color?: string; thickness?: number; pulse?: boolean; pulseDuration?: number; style?: CSSProperties};
/** Coordinates use the explicit SVG viewBox; pass the same size as the containing world. */
export function Connector({from, to, width = 1280, height = 720, bend = 0, color = "#777", thickness = 3, pulse = true, pulseDuration = 1.2, at = 0, duration = 0.7, style}: ConnectorProps) {
  const frame = useCurrentFrame(), {fps} = useVideoConfig();
  positive(width, "width"); positive(height, "height"); positive(thickness, "thickness"); positive(pulseDuration, "pulseDuration");
  const geometry = connectorGeometry(from, to, bend), p = reveal(frame, fps, at, duration);
  const elapsed = Math.max(0, frame / fps - at - duration);
  const pulseAt = phase(elapsed % pulseDuration, 1, 0, pulseDuration), dot = geometry.point(pulseAt);
  return <svg width={width} height={height} viewBox={`0 0 ${width} ${height}`} style={{overflow: "visible", ...style}}>
    {p > 0 && <path d={geometry.path} fill="none" stroke={color} strokeWidth={thickness} strokeLinecap="round" pathLength={1} strokeDasharray={1} strokeDashoffset={1 - p} />}
    {pulse && p === 1 && <circle cx={dot.x} cy={dot.y} r={thickness * 1.8} fill={color} />}
  </svg>;
}
