import { defineStory, useSelect, useBoolean } from '@djangocfg/playground';
import PrettyCode from './index';

export default defineStory({
  title: 'Tools/Pretty Code',
  component: PrettyCode,
  description: 'Syntax highlighted code block with copy button.',
});

// ─── Samples ──────────────────────────────────────────────────────────────────

const LONG_CODE_SAMPLES = {
  typescript: `import { useCallback, useEffect, useMemo, useRef, useState } from 'react';

export type Status = 'idle' | 'loading' | 'success' | 'error';

export interface PaginationParams {
  page: number;
  pageSize: number;
  sortBy?: string;
  sortOrder?: 'asc' | 'desc';
}

export function useVehicles(initialParams?: Partial<PaginationParams>) {
  const [status, setStatus] = useState<Status>('idle');
  const [data, setData] = useState<unknown[]>([]);
  const [error, setError] = useState<Error | null>(null);
  const abortRef = useRef<AbortController | null>(null);

  const [params, setParams] = useState<PaginationParams>({
    page: 1,
    pageSize: 20,
    ...initialParams,
  });

  const fetch = useCallback(async (p: PaginationParams) => {
    abortRef.current?.abort();
    abortRef.current = new AbortController();
    setStatus('loading');
    try {
      const query = new URLSearchParams({
        page: String(p.page),
        pageSize: String(p.pageSize),
      });
      const res = await window.fetch(\`/api/vehicles?\${query}\`, {
        signal: abortRef.current.signal,
      });
      if (!res.ok) throw new Error(\`HTTP \${res.status}\`);
      const json = await res.json();
      setData(json.data);
      setStatus('success');
    } catch (err) {
      if ((err as Error).name === 'AbortError') return;
      setError(err as Error);
      setStatus('error');
    }
  }, []);

  useEffect(() => {
    fetch(params);
    return () => abortRef.current?.abort();
  }, [params, fetch]);

  return { status, data, error, params, setParams };
}`,

  python: `from __future__ import annotations

import asyncio
import logging
from dataclasses import dataclass, field
from datetime import datetime
from enum import StrEnum

import httpx
from pydantic import BaseModel, ConfigDict, Field, field_validator

logger = logging.getLogger(__name__)


class FuelType(StrEnum):
    GASOLINE = "gasoline"
    DIESEL = "diesel"
    ELECTRIC = "electric"


class Vehicle(BaseModel):
    model_config = ConfigDict(from_attributes=True)

    id: str
    make: str
    model: str
    year: int
    fuel_type: FuelType
    created_at: datetime = Field(default_factory=datetime.utcnow)

    @field_validator("year")
    @classmethod
    def validate_year(cls, v: int) -> int:
        if not (1900 <= v <= datetime.utcnow().year + 1):
            raise ValueError(f"Invalid year: {v}")
        return v


@dataclass
class VehicleRepository:
    base_url: str

    async def list(self, page: int = 1, page_size: int = 20) -> list[Vehicle]:
        async with httpx.AsyncClient(base_url=self.base_url) as client:
            response = await client.get(
                "/vehicles",
                params={"page": page, "page_size": page_size},
            )
            response.raise_for_status()
            payload = response.json()
            return [Vehicle.model_validate(v) for v in payload["data"]]`,

  javascript: `class EventBus {
  #listeners = new Map();
  #wildcards = new Set();

  on(event, handler) {
    if (event === '*') {
      this.#wildcards.add(handler);
      return () => this.#wildcards.delete(handler);
    }
    if (!this.#listeners.has(event)) {
      this.#listeners.set(event, new Set());
    }
    this.#listeners.get(event).add(handler);
    return () => this.#listeners.get(event)?.delete(handler);
  }

  once(event, handler) {
    const unsub = this.on(event, (...args) => {
      unsub();
      handler(...args);
    });
    return unsub;
  }

  emit(event, payload) {
    this.#listeners.get(event)?.forEach(h => h(payload));
    this.#wildcards.forEach(h => h(event, payload));
  }
}

const bus = new EventBus();
bus.on('*', (event, payload) => console.debug(\`[bus] \${event}\`, payload));
bus.emit('vehicle:created', { id: 'v_001', make: 'BMW' });`,

  css: `:root {
  --radius-md: 0.5rem;
  --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
  --transition-base: 200ms cubic-bezier(0.4, 0, 0.2, 1);
}

.card {
  border-radius: var(--radius-md);
  border: 1px solid hsl(var(--border));
  background: hsl(var(--card));
  box-shadow: var(--shadow-sm);
  transition: box-shadow var(--transition-base);

  &:hover {
    box-shadow: var(--shadow-md);
    transform: translateY(-1px);
  }
}

@keyframes shimmer {
  from { background-position: -200% 0; }
  to   { background-position:  200% 0; }
}

.card--skeleton .card-title {
  background: linear-gradient(90deg, hsl(var(--muted)) 25%, hsl(var(--muted-foreground) / 0.1) 50%, hsl(var(--muted)) 75%);
  background-size: 200% 100%;
  animation: shimmer 1.5s infinite;
}`,

  html: `<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <title>Sample</title>
</head>
<body>
  <div id="root"></div>
  <script type="module" src="/entry.tsx"></script>
</body>
</html>`,
};

// ─── Stories ──────────────────────────────────────────────────────────────────

/** Interactive — language, variant, compact, scrollIsolation all toggleable.
 *  Replaces the old per-language / per-mode / per-flag stories which were
 *  just this component with different defaults. */
export const Interactive = () => {
  const [language] = useSelect('language', {
    options: ['typescript', 'javascript', 'python', 'css', 'html'] as const,
    defaultValue: 'typescript',
    label: 'Language',
  });

  const [variant] = useSelect('variant', {
    options: ['card', 'plain'] as const,
    defaultValue: 'card',
    label: 'Variant',
    description: 'card = full chrome, plain = bare highlighted code',
  });

  const [isCompact] = useBoolean('isCompact', {
    defaultValue: false,
    label: 'Compact',
  });

  const [scrollIsolation] = useBoolean('scrollIsolation', {
    defaultValue: true,
    label: 'Scroll isolation',
    description: 'Require click before the block captures scroll (card only)',
  });

  return (
    <PrettyCode
      data={LONG_CODE_SAMPLES[language]}
      language={language}
      variant={variant}
      isCompact={isCompact}
      scrollIsolation={scrollIsolation}
    />
  );
};

/** Variants — ``card`` vs ``plain`` side by side. The card has its own
 *  border, hover toolbar, and can internally scroll when a ``maxLines``
 *  cap is hit. The plain variant is chrome-less for embedding inside
 *  an existing scroll container (e.g. a response panel), where the
 *  parent surface owns chrome and scroll. */
export const Variants = () => (
  <div className="grid grid-cols-1 lg:grid-cols-2 gap-4 max-w-6xl">
    <section className="space-y-2">
      <h3 className="text-sm font-semibold">variant=&quot;card&quot; (default)</h3>
      <p className="text-xs text-muted-foreground">
        Border, background, hover toolbar with Copy + language tag.
      </p>
      <PrettyCode data={LONG_CODE_SAMPLES.typescript} language="typescript" variant="card" />
    </section>
    <section className="space-y-2">
      <h3 className="text-sm font-semibold">variant=&quot;plain&quot;</h3>
      <p className="text-xs text-muted-foreground">
        Chrome-less. No border, no background, no toolbar. Flows into the
        parent scroll container.
      </p>
      <PrettyCode data={LONG_CODE_SAMPLES.typescript} language="typescript" variant="plain" />
    </section>
  </div>
);

/** LongCode — stress test for multi-column layouts and long snippets. */
export const LongCode = () => (
  <div className="flex flex-col gap-6 max-w-4xl">
    <div className="h-96">
      <PrettyCode data={LONG_CODE_SAMPLES.typescript} language="typescript" maxLines={30} />
    </div>
    <div className="h-96">
      <PrettyCode data={LONG_CODE_SAMPLES.python} language="python" maxLines={30} />
    </div>
    <div className="h-96">
      <PrettyCode data={LONG_CODE_SAMPLES.javascript} language="javascript" maxLines={30} />
    </div>
  </div>
);

/** ScrollIsolation — both states side-by-side so the difference is
 *  obvious without toggling. The isolation flag matters most when the
 *  block is embedded in a scrolling page: without it, wheel-scrolling
 *  anywhere over the block "captures" the wheel and the page itself
 *  stops scrolling — click-to-unlock avoids that surprise. */
export const ScrollIsolation = () => (
  <div className="flex flex-col gap-8 max-w-4xl">
    <section className="space-y-2">
      <h3 className="text-sm font-semibold">scrollIsolation=true (default)</h3>
      <p className="text-xs text-muted-foreground">
        Hover to see the hint; click to unlock wheel-scroll inside the block.
      </p>
      <div className="h-64">
        <PrettyCode data={LONG_CODE_SAMPLES.typescript} language="typescript" maxLines={15} scrollIsolation />
      </div>
    </section>
    <section className="space-y-2">
      <h3 className="text-sm font-semibold">scrollIsolation=false</h3>
      <p className="text-xs text-muted-foreground">
        Wheel-scroll captured immediately — use when the block has no
        surrounding page-level scroll to compete with.
      </p>
      <div className="h-64">
        <PrettyCode data={LONG_CODE_SAMPLES.typescript} language="typescript" maxLines={15} scrollIsolation={false} />
      </div>
    </section>
  </div>
);
