-- Migration 025: Blog Self-Learning Loop (SLL v1.0)
-- Tracks LinkedIn engagement per post, extracts winning/losing patterns,
-- feeds insights back into the generation pipeline automatically.

-- ─────────────────────────────────────────────────────────
-- Blog Performance: LinkedIn/platform engagement per post
-- ─────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS blog_performance (
  id                UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  blog_id           UUID REFERENCES blog_drafts(id) ON DELETE CASCADE,
  platform          TEXT DEFAULT 'linkedin',

  -- Raw engagement metrics (LinkedIn: saves > shares > comments > likes)
  impressions       INTEGER,
  comments          INTEGER DEFAULT 0,
  shares            INTEGER DEFAULT 0,
  saves             INTEGER DEFAULT 0,
  likes             INTEGER DEFAULT 0,

  -- SLL score formula: (comments×3) + (shares×2) + (saves×2), likes = 0 weight
  engagement_score  INTEGER GENERATED ALWAYS AS (
    COALESCE(comments,0)*3 + COALESCE(shares,0)*2 + COALESCE(saves,0)*2
  ) STORED,

  -- Snapshot of article at time of measurement (for pattern extraction)
  hook_text         TEXT,        -- first ~120 chars of the LinkedIn post
  blog_type         TEXT,        -- tutorial | market_alert | comparison | …
  topic             TEXT,        -- topic tag used during generation
  word_count        INTEGER,
  pipeline_version  TEXT,

  -- Timing
  posted_at         TIMESTAMPTZ,
  measured_at       TIMESTAMPTZ DEFAULT NOW(),
  notes             TEXT,
  created_at        TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX IF NOT EXISTS idx_blog_perf_blog    ON blog_performance(blog_id);
CREATE INDEX IF NOT EXISTS idx_blog_perf_score   ON blog_performance(engagement_score DESC);
CREATE INDEX IF NOT EXISTS idx_blog_perf_type    ON blog_performance(blog_type);
CREATE INDEX IF NOT EXISTS idx_blog_perf_posted  ON blog_performance(posted_at DESC);

-- ─────────────────────────────────────────────────────────
-- Learned Patterns: extracted winning / losing patterns
-- ─────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS blog_learned_patterns (
  id                  UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  pattern_type        TEXT NOT NULL,   -- hook | structure | topic | length | opening | verb_style
  pattern_value       TEXT NOT NULL,   -- "short hook + contrast", "lab vs production", "single scenario"
  performance_class   TEXT NOT NULL,   -- winner | loser
  avg_engagement      NUMERIC,         -- average engagement_score across samples
  sample_count        INTEGER DEFAULT 1,
  example_post_id     UUID REFERENCES blog_drafts(id),
  active              BOOLEAN DEFAULT TRUE,
  extracted_at        TIMESTAMPTZ DEFAULT NOW(),
  last_updated        TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX IF NOT EXISTS idx_blog_patterns_type  ON blog_learned_patterns(pattern_type);
CREATE INDEX IF NOT EXISTS idx_blog_patterns_class ON blog_learned_patterns(performance_class);
CREATE INDEX IF NOT EXISTS idx_blog_patterns_active ON blog_learned_patterns(active) WHERE active = TRUE;

-- ─────────────────────────────────────────────────────────
-- SLL State: current active learning snapshot (weekly)
-- ─────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS blog_sll_state (
  id                    UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  week_start            DATE NOT NULL UNIQUE,

  -- Top-performing patterns (JSON arrays of strings)
  winner_patterns       JSONB DEFAULT '[]',
  loser_patterns        JSONB DEFAULT '[]',

  -- Topic performance
  top_topics            JSONB DEFAULT '[]',
  avoid_topics          JSONB DEFAULT '[]',

  -- Length optimization
  optimal_length_min    INTEGER,
  optimal_length_max    INTEGER,

  -- Hook patterns that convert
  best_hook_patterns    JSONB DEFAULT '[]',

  -- Meta
  posts_analyzed        INTEGER DEFAULT 0,
  avg_engagement_score  NUMERIC,
  generated_at          TIMESTAMPTZ DEFAULT NOW(),
  generated_by          TEXT DEFAULT 'sll-auto'
);

-- ─────────────────────────────────────────────────────────
-- View: performance enriched with blog metadata
-- ─────────────────────────────────────────────────────────
CREATE OR REPLACE VIEW v_blog_performance AS
SELECT
  p.id,
  p.blog_id,
  d.title,
  d.topic,
  d.pipeline_version,
  d.word_count,
  p.platform,
  p.impressions,
  p.comments,
  p.shares,
  p.saves,
  p.likes,
  p.engagement_score,
  p.hook_text,
  p.blog_type,
  p.posted_at,
  p.measured_at,
  -- Performance tier based on engagement score
  CASE
    WHEN p.engagement_score >= 20 THEN 'gold'
    WHEN p.engagement_score >= 10 THEN 'silver'
    WHEN p.engagement_score >= 4  THEN 'bronze'
    ELSE 'miss'
  END AS performance_tier
FROM blog_performance p
JOIN blog_drafts d ON d.id = p.blog_id;

COMMENT ON TABLE blog_performance IS 'SLL v1.0: LinkedIn engagement tracking per blog post';
COMMENT ON TABLE blog_learned_patterns IS 'SLL v1.0: Extracted winning/losing content patterns';
COMMENT ON TABLE blog_sll_state IS 'SLL v1.0: Weekly learning state snapshot for pipeline injection';
