-- ============================================================
-- WM 2026 Tippspiel – Datenbankschema für Supabase / PostgreSQL
-- ============================================================
-- Ausführen in Supabase SQL Editor oder via psql:
--   psql $DATABASE_URL -f schema.sql
-- ============================================================

-- Extensions
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";

-- ============================================================
-- USERS
-- Staffbase userId (sub) als Primary Key
-- Wird beim ersten Login automatisch angelegt
-- ============================================================
CREATE TABLE IF NOT EXISTS users (
  id          TEXT PRIMARY KEY,           -- Staffbase userId (JWT sub)
  full_name   TEXT NOT NULL DEFAULT '',
  locale      TEXT NOT NULL DEFAULT 'de_DE',
  branch_id   TEXT,
  role        TEXT NOT NULL DEFAULT 'viewer',
  created_at  TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  updated_at  TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

-- ============================================================
-- MATCHES
-- Gespiegelte Daten von football-data.org
-- Wird per Cronjob synchronisiert
-- ============================================================
CREATE TABLE IF NOT EXISTS matches (
  id                 SERIAL PRIMARY KEY,
  external_id        INTEGER UNIQUE NOT NULL, -- football-data.org Match ID
  utc_date           TIMESTAMPTZ NOT NULL,
  status             TEXT NOT NULL DEFAULT 'SCHEDULED',
  stage              TEXT NOT NULL,           -- 'GROUP_STAGE', 'ROUND_OF_16', etc.
  group_name         TEXT,                    -- 'GROUP_A', NULL für KO-Runden
  home_team_id       INTEGER NOT NULL,
  away_team_id       INTEGER NOT NULL,
  home_team_name     TEXT NOT NULL,
  away_team_name     TEXT NOT NULL,
  home_team_short    TEXT NOT NULL DEFAULT '',
  away_team_short    TEXT NOT NULL DEFAULT '',
  home_team_crest    TEXT,                    -- URL zum Team-Logo
  away_team_crest    TEXT,
  score_home         INTEGER,                 -- NULL = noch nicht gespielt
  score_away         INTEGER,
  created_at         TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  updated_at         TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE INDEX IF NOT EXISTS idx_matches_utc_date ON matches(utc_date);
CREATE INDEX IF NOT EXISTS idx_matches_status ON matches(status);
CREATE INDEX IF NOT EXISTS idx_matches_stage ON matches(stage);

-- ============================================================
-- TIPS
-- Tipp eines Users für ein Spiel
-- Unique Constraint: 1 Tipp pro User pro Spiel
-- ============================================================
CREATE TABLE IF NOT EXISTS tips (
  id         SERIAL PRIMARY KEY,
  user_id    TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
  match_id   INTEGER NOT NULL REFERENCES matches(id) ON DELETE CASCADE,
  tip_home   SMALLINT NOT NULL CHECK (tip_home >= 0),
  tip_away   SMALLINT NOT NULL CHECK (tip_away >= 0),
  points     SMALLINT,                        -- NULL = Spiel noch nicht ausgewertet
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),

  CONSTRAINT tips_unique_user_match UNIQUE (user_id, match_id)
);

CREATE INDEX IF NOT EXISTS idx_tips_user_id ON tips(user_id);
CREATE INDEX IF NOT EXISTS idx_tips_match_id ON tips(match_id);
CREATE INDEX IF NOT EXISTS idx_tips_points ON tips(points);

-- ============================================================
-- LEADERBOARD (Materialized View)
-- Wird nach jeder Punkte-Berechnung refreshed
-- ============================================================
CREATE MATERIALIZED VIEW IF NOT EXISTS leaderboard AS
SELECT
  u.id                                    AS user_id,
  u.full_name,
  COALESCE(SUM(t.points), 0)::INTEGER    AS total_points,
  COUNT(t.id)::INTEGER                   AS tips_count,
  COUNT(CASE WHEN t.points = 3 THEN 1 END)::INTEGER AS exact_count,
  COUNT(CASE WHEN t.points = 1 THEN 1 END)::INTEGER AS tendency_count,
  RANK() OVER (
    ORDER BY COALESCE(SUM(t.points), 0) DESC,
             COUNT(CASE WHEN t.points = 3 THEN 1 END) DESC
  )::INTEGER AS rank
FROM users u
LEFT JOIN tips t ON t.user_id = u.id
GROUP BY u.id, u.full_name
ORDER BY total_points DESC, exact_count DESC, u.full_name;

CREATE UNIQUE INDEX IF NOT EXISTS idx_leaderboard_user_id
  ON leaderboard(user_id);

-- ============================================================
-- SYNC LOG
-- Protokolliert die football-data.org Sync-Läufe
-- ============================================================
CREATE TABLE IF NOT EXISTS sync_log (
  id            SERIAL PRIMARY KEY,
  synced_at     TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  matches_total INTEGER NOT NULL DEFAULT 0,
  matches_new   INTEGER NOT NULL DEFAULT 0,
  matches_upd   INTEGER NOT NULL DEFAULT 0,
  status        TEXT NOT NULL DEFAULT 'success', -- 'success' | 'error'
  error_msg     TEXT
);

-- ============================================================
-- TRIGGER: updated_at automatisch setzen
-- ============================================================
CREATE OR REPLACE FUNCTION set_updated_at()
RETURNS TRIGGER AS $$
BEGIN
  NEW.updated_at = NOW();
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER users_updated_at
  BEFORE UPDATE ON users
  FOR EACH ROW EXECUTE FUNCTION set_updated_at();

CREATE TRIGGER matches_updated_at
  BEFORE UPDATE ON matches
  FOR EACH ROW EXECUTE FUNCTION set_updated_at();

CREATE TRIGGER tips_updated_at
  BEFORE UPDATE ON tips
  FOR EACH ROW EXECUTE FUNCTION set_updated_at();

-- ============================================================
-- ROW LEVEL SECURITY (Supabase)
-- Wichtig: Verhindert direkten Frontend-Zugriff auf DB
-- Alles läuft über den Backend-Service mit service_role key
-- ============================================================
ALTER TABLE users ENABLE ROW LEVEL SECURITY;
ALTER TABLE matches ENABLE ROW LEVEL SECURITY;
ALTER TABLE tips ENABLE ROW LEVEL SECURITY;

-- Service Role darf alles
CREATE POLICY "service_role_all" ON users
  FOR ALL USING (auth.role() = 'service_role');
CREATE POLICY "service_role_all" ON matches
  FOR ALL USING (auth.role() = 'service_role');
CREATE POLICY "service_role_all" ON tips
  FOR ALL USING (auth.role() = 'service_role');
