/**
 * Types for the Awwwards eye-library ingester.
 *
 * The base library only accepts SOTY, SOTM, and SOTD. Other Awwwards
 * tiers (Honorable Mention, Nominee, category Honors) are out of scope
 * for the base ship — users add those manually via their personal filter.
 */

import { z } from 'zod'

/**
 * Award tiers accepted into the base library. Weights drive critic scoring.
 */
export const AWARD_TIERS = ['SOTY', 'SOTM', 'SOTD'] as const
export type AwardTier = (typeof AWARD_TIERS)[number]

export const TIER_WEIGHTS: Readonly<Record<AwardTier, number>> = {
  SOTY: 1.0,
  SOTM: 0.85,
  SOTD: 0.7,
}

/**
 * Personal filter state applied by the user after ingest.
 */
export const FILTER_STATES = ['love', 'keep', 'unseen', 'reject'] as const
export type FilterState = (typeof FILTER_STATES)[number]

export const FILTER_MULTIPLIERS: Readonly<Record<FilterState, number>> = {
  love: 1.3,
  keep: 1.1,
  unseen: 1.0,
  reject: 0.0,
}

/**
 * Metadata scraped from an Awwwards gallery card.
 * Every field is what the card surface reveals; richer metadata
 * (jury subscores, creator bio) is fetched lazily from the detail page.
 */
export const awwwardsSiteSchema = z.object({
  slug: z.string().min(1),
  title: z.string().min(1),
  agency: z.string().optional(),
  liveUrl: z.string().url(),
  detailUrl: z.string().url(),
  tier: z.enum(AWARD_TIERS),
  awardDate: z.string(),
  industryTags: z.array(z.string()).default([]),
  techTags: z.array(z.string()).default([]),
  styleTags: z.array(z.string()).default([]),
  colorTags: z.array(z.string()).default([]),
  country: z.string().optional(),
})

export type AwwwardsSite = z.infer<typeof awwwardsSiteSchema>

/**
 * A single frame's motion descriptors, derived from keyframe analysis.
 * Values are structural signatures the critic uses to compare generated
 * output against the love cluster — no aesthetic judgment here.
 */
export const motionDescriptorsSchema = z.object({
  durationMs: z.number().int().nonnegative(),
  keyframeCount: z.number().int().nonnegative(),
  /** DOM transform delta per scroll pixel, averaged across scroll capture. */
  scrollLinkedRatio: z.number().nonnegative(),
  /** Time between hover trigger and first visible response, in ms. */
  hoverLatencyMs: z.number().nonnegative().optional(),
  /** Primary hover transition duration, in ms. */
  hoverDurationMs: z.number().nonnegative().optional(),
  /** Closest named cubic-bezier curve from the 20-curve bank. */
  primaryEasing: z.string().optional(),
  /** FPS variance across capture — low value = stable motion. */
  fpsVariance: z.number().nonnegative(),
  /** Median FPS across capture. */
  fpsMedian: z.number().nonnegative(),
  /** Fraction of viewport covered by canvas/video — 3D/WebGL signal. */
  canvasCoverage: z.number().min(0).max(1),
  /** Fraction of captured frames showing visible change. */
  animationDensity: z.number().min(0).max(1),
})

export type MotionDescriptors = z.infer<typeof motionDescriptorsSchema>

/**
 * Per-site ingest record persisted alongside the captured media.
 */
export const siteRecordSchema = z.object({
  site: awwwardsSiteSchema,
  capturedAt: z.string().datetime(),
  captureStatus: z.enum(['ok', 'partial', 'failed']),
  filterState: z.enum(FILTER_STATES).default('unseen'),
  captureErrors: z.array(z.string()).default([]),
  /** Relative paths from the site's directory. */
  files: z.object({
    static: z.string().optional(),
    motion: z.string().optional(),
    keyframes: z.array(z.string()).default([]),
  }),
})

export type SiteRecord = z.infer<typeof siteRecordSchema>

/**
 * Configuration for an ingest run.
 */
export interface IngestRunConfig {
  /** Which tiers to pull. Base library: only SOTY/SOTM/SOTD. */
  tiers: readonly AwardTier[]
  /** Max pages to fetch per tier. 0 = all available. */
  maxPagesPerTier: number
  /** Max concurrent captures. Polite default: 2. */
  concurrency: number
  /** Milliseconds between site fetches. Polite default: 2000. */
  requestDelayMs: number
  /** Skip sites already captured within this many days. */
  reingestAfterDays: number
  /** User-agent string for Awwwards requests. */
  userAgent: string
}

export const DEFAULT_INGEST_CONFIG: IngestRunConfig = {
  tiers: AWARD_TIERS,
  maxPagesPerTier: 0,
  concurrency: 2,
  requestDelayMs: 2000,
  reingestAfterDays: 90,
  userAgent: 'SANDCODE/eye-ingester (https://github.com/Djsand/SANDCODE; local research tool)',
}
