/**
 * CanvasViewport — Virtualized scrolling canvas.
 * Renders CanvasComponent objects sorted by layout.orderIndex.
 * Uses CSS content-visibility for large component lists (virtualization-lite).
 * Stable scroll anchoring: new components append without scroll jump.
 */
import { useRef } from 'react';
import { AnimatePresence, motion } from 'framer-motion';
import type { CanvasComponent, PromptExecution } from '../types/canvas';
import { ComponentRegistry, type ComponentRenderContext } from './ComponentRegistry';
import { Loader2 } from 'lucide-react';

interface CanvasViewportProps {
  components: CanvasComponent[];
  ctx: ComponentRenderContext;
  inFlightExecution: PromptExecution | null;
  selectedComponentId?: string | null;
  onSelectComponent: (id: string | null) => void;
}

const SECTION_LABELS: Record<string, string> = {
  sec_pipeline: 'Pipeline',
  sec_leads: 'Lead Intelligence',
  sec_team: 'Team Performance',
  sec_actions: 'Action Queue',
  sec_prompt_generated: 'Oracle Responses',
  sec_geography: 'Geographic',
  sec_forecast: 'Forecasting',
};

/** Groups components by sectionId, preserving orderIndex sort */
function groupBySection(components: CanvasComponent[]): Array<{ sectionId: string; components: CanvasComponent[] }> {
  const sorted = [...components].sort((a, b) => a.layout.orderIndex - b.layout.orderIndex);
  const sectionMap = new Map<string, CanvasComponent[]>();

  for (const comp of sorted) {
    const sid = comp.layout.sectionId;
    if (!sectionMap.has(sid)) sectionMap.set(sid, []);
    sectionMap.get(sid)!.push(comp);
  }

  return Array.from(sectionMap.entries())
    .map(([sectionId, comps]) => ({ sectionId, components: comps }))
    .sort((a, b) => {
      const aPrompt = a.sectionId.startsWith('sec_prompt_generated');
      const bPrompt = b.sectionId.startsWith('sec_prompt_generated');
      if (aPrompt && bPrompt) {
        const aCreated = Math.max(...a.components.map((comp) => Date.parse(comp.provenance.createdAt || '1970-01-01T00:00:00Z')));
        const bCreated = Math.max(...b.components.map((comp) => Date.parse(comp.provenance.createdAt || '1970-01-01T00:00:00Z')));
        return bCreated - aCreated;
      }
      if (aPrompt !== bPrompt) return aPrompt ? -1 : 1;
      return Math.min(...a.components.map((comp) => comp.layout.orderIndex)) - Math.min(...b.components.map((comp) => comp.layout.orderIndex));
    });
}

function getSectionLabel(sectionId: string, sectionComps: CanvasComponent[]): string {
  if (SECTION_LABELS[sectionId]) return SECTION_LABELS[sectionId];
  if (sectionId.startsWith('sec_prompt_generated')) {
    const planning = sectionComps.find((comp) => comp.type === 'textCanvas');
    const content = planning?.visualizationParameters?.content;
    if (typeof content === 'string') {
      const firstLine = content.split('\n')[0]?.trim();
      if (firstLine?.startsWith('Oracle received:')) {
        return firstLine.replace('Oracle received:', '').trim();
      }
    }
    return 'Oracle Response';
  }
  return sectionId.replace(/^sec_/, '').replace(/_/g, ' ');
}

/** CSS content-visibility wrapper for off-screen components, applying width mode to the flex item */
function ComponentFlexWrapper({ comp, children }: { comp: CanvasComponent; children: React.ReactNode }) {
  const styles: Record<string, string> = {
    full: 'w-full',
    half: 'w-full xl:w-[calc(50%-8px)]',
    third: 'w-full xl:w-[calc(33.333%-11px)]',
  };
  return (
    <div
      className={styles[comp.layout.widthMode] ?? 'w-full'}
      style={{
        contentVisibility: 'auto',
        containIntrinsicSize: `0 ${comp.renderingHints.estimatedHeightPx}px`,
      }}
    >
      {children}
    </div>
  );
}

export function CanvasViewport({
  components,
  ctx,
  inFlightExecution,
  selectedComponentId,
  onSelectComponent,
}: CanvasViewportProps) {
  const viewportRef = useRef<HTMLDivElement>(null);
  const sections = groupBySection(components);

  return (
    <div
      ref={viewportRef}
      className="flex-1 overflow-y-auto overflow-x-hidden custom-scrollbar"
      style={{ scrollBehavior: 'smooth' }}
      onClick={(e) => {
        // Click outside component to deselect
        if (e.currentTarget === e.target) onSelectComponent(null);
      }}
    >
      <div className="px-4 py-5 space-y-8">
        {sections.length === 0 && !inFlightExecution && (
          <EmptyCanvasState />
        )}

        {sections.map(({ sectionId, components: sectionComps }) => (
          <section key={sectionId} className="space-y-4">
            {/* Section header */}
            <div className="flex items-center gap-3">
              <div className="w-1 h-4 rounded-full bg-gradient-to-b from-blue-400 to-cyan-500" />
              <h2 className="text-xs font-semibold uppercase tracking-widest text-zinc-500">
                {getSectionLabel(sectionId, sectionComps)}
              </h2>
              <div className="flex-1 h-[1px]" style={{ background: 'rgba(255,255,255,0.05)' }} />
              <span className="text-[10px] text-zinc-700">{sectionComps.length}</span>
            </div>

            {/* Flex wrap for half/third width components */}
            <div className="flex flex-wrap gap-4">
              {sectionComps.map((comp) => (
                <ComponentFlexWrapper
                  key={`${comp.componentId}-${comp.version}`}
                  comp={comp}
                >
                  <ComponentRegistry
                    component={comp}
                    ctx={{
                      ...ctx,
                      isSelected: selectedComponentId === comp.componentId,
                      onSelect: onSelectComponent,
                    }}
                  />
                </ComponentFlexWrapper>
              ))}
            </div>
          </section>
        ))}

        {/* In-flight execution placeholder */}
        <AnimatePresence>
          {inFlightExecution && (
            <motion.div
              key="in-flight-placeholder"
              initial={{ opacity: 0, y: 16 }}
              animate={{ opacity: 1, y: 0 }}
              exit={{ opacity: 0, y: -8 }}
              className="w-full rounded-2xl p-6 flex items-center gap-4"
              style={{
                background: 'rgba(59,130,246,0.06)',
                border: '1px dashed rgba(59,130,246,0.25)',
                backdropFilter: 'blur(12px)',
              }}
            >
              <Loader2 className="w-5 h-5 text-blue-400 animate-spin flex-shrink-0" />
              <div>
                <p className="text-sm font-medium text-blue-300">Oracle is analyzing your prompt…</p>
                <p className="text-xs text-zinc-500 mt-0.5 line-clamp-1">
                  "{inFlightExecution.prompt}"
                </p>
              </div>
              <div
                className="ml-auto text-xs font-mono px-2 py-1 rounded-lg"
                style={{ background: 'rgba(59,130,246,0.12)', color: '#60a5fa' }}
              >
                {inFlightExecution.status}
              </div>
            </motion.div>
          )}
        </AnimatePresence>

        {/* Bottom padding for floating prompt bar */}
        <div className="h-32" />
      </div>
    </div>
  );
}

function EmptyCanvasState() {
  return (
    <div className="flex flex-col items-center justify-center py-24 gap-6">
      <div
        className="w-20 h-20 rounded-2xl flex items-center justify-center"
        style={{ background: 'rgba(59,130,246,0.08)', border: '1px solid rgba(59,130,246,0.15)' }}
      >
        <svg viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg" className="w-10 h-10">
          <rect x="6" y="6" width="15" height="15" rx="3" stroke="#3B82F6" strokeWidth="1.5" />
          <rect x="27" y="6" width="15" height="15" rx="3" stroke="#22D3EE" strokeWidth="1.5" />
          <rect x="6" y="27" width="15" height="15" rx="3" stroke="#A78BFA" strokeWidth="1.5" />
          <rect x="27" y="27" width="15" height="15" rx="3" stroke="#3B82F6" strokeWidth="1.5" opacity="0.5" strokeDasharray="3 2" />
        </svg>
      </div>
      <div className="text-center">
        <h3 className="text-base font-medium text-zinc-200 mb-2">Canvas is empty</h3>
        <p className="text-sm text-zinc-500 max-w-sm">
          Ask Oracle anything — type a prompt below to generate analytical components on your canvas.
        </p>
      </div>
    </div>
  );
}
