const fs = require('fs');
const path = require('path');

const migrationPath = path.join(__dirname, '../../supabase/migrations/001_initial_schema.sql');
const sql = fs.readFileSync(migrationPath, 'utf8');

describe('Database Schema (001_initial_schema.sql)', () => {
  describe('Table definitions', () => {
    const expectedTables = ['users', 'picks', 'scan_sessions', 'bets', 'outcomes', 'performance'];

    test.each(expectedTables)('defines CREATE TABLE for %s', (table) => {
      const regex = new RegExp(`CREATE TABLE public\\.${table}\\s*\\(`, 'i');
      expect(sql).toMatch(regex);
    });
  });

  describe('RLS enabled', () => {
    const tables = ['users', 'picks', 'scan_sessions', 'bets', 'outcomes', 'performance'];

    test.each(tables)('enables RLS on %s', (table) => {
      expect(sql).toContain(`ALTER TABLE public.${table} ENABLE ROW LEVEL SECURITY`);
    });
  });

  describe('RLS policies', () => {
    test('users has select, update, insert policies', () => {
      expect(sql).toContain('CREATE POLICY "users_select_own"');
      expect(sql).toContain('CREATE POLICY "users_update_own"');
      expect(sql).toContain('CREATE POLICY "users_insert_own"');
    });

    test('picks has select and insert policies', () => {
      expect(sql).toContain('CREATE POLICY "picks_select_own"');
      expect(sql).toContain('CREATE POLICY "picks_insert_own"');
    });

    test('scan_sessions has select and insert policies', () => {
      expect(sql).toContain('CREATE POLICY "scan_sessions_select_own"');
      expect(sql).toContain('CREATE POLICY "scan_sessions_insert_own"');
    });

    test('bets has select, insert, and update policies', () => {
      expect(sql).toContain('CREATE POLICY "bets_select_own"');
      expect(sql).toContain('CREATE POLICY "bets_insert_own"');
      expect(sql).toContain('CREATE POLICY "bets_update_own"');
    });

    test('outcomes has select policy with picks join', () => {
      expect(sql).toContain('CREATE POLICY "outcomes_select_own"');
      expect(sql).toContain('picks.user_id = auth.uid()');
    });

    test('performance has select policy', () => {
      expect(sql).toContain('CREATE POLICY "performance_select_own"');
    });

    test('all policies use auth.uid()', () => {
      const policyBlocks = sql.match(/CREATE POLICY[\s\S]*?;/g) || [];
      for (const block of policyBlocks) {
        expect(block).toContain('auth.uid()');
      }
    });
  });

  describe('Constraints', () => {
    test('users.tier constrained to free/analyst/desk', () => {
      expect(sql).toMatch(/tier.*CHECK.*\(tier IN \('free',\s*'analyst',\s*'desk'\)\)/);
    });

    test('picks.grade constrained to A/B/C/D', () => {
      expect(sql).toMatch(/grade.*CHECK.*\(grade IN \('A',\s*'B',\s*'C',\s*'D'\)\)/);
    });

    test('picks.direction constrained to over/under', () => {
      expect(sql).toMatch(/direction.*CHECK.*\(direction IN \('over',\s*'under'\)\)/);
    });

    test('bets.bet_type constrained', () => {
      expect(sql).toMatch(/bet_type.*CHECK.*\(bet_type IN/);
    });

    test('bets.status constrained', () => {
      expect(sql).toMatch(/status.*CHECK.*\(status IN/);
    });

    test('outcomes.result constrained to hit/miss/push', () => {
      expect(sql).toMatch(/result.*CHECK.*\(result IN \('hit',\s*'miss',\s*'push'\)\)/);
    });

    test('performance.period constrained', () => {
      expect(sql).toMatch(/period.*CHECK.*\(period IN/);
    });
  });

  describe('Indexes', () => {
    test('picks has user_id and created_at indexes', () => {
      expect(sql).toContain('CREATE INDEX idx_picks_user_id');
      expect(sql).toContain('CREATE INDEX idx_picks_created_at');
    });

    test('bets has user_id, status, and placed_at indexes', () => {
      expect(sql).toContain('CREATE INDEX idx_bets_user_id');
      expect(sql).toContain('CREATE INDEX idx_bets_status');
      expect(sql).toContain('CREATE INDEX idx_bets_placed_at');
    });

    test('outcomes has unique index on pick_id', () => {
      expect(sql).toContain('CREATE UNIQUE INDEX idx_outcomes_pick_id');
    });

    test('performance has unique index on user_id + period', () => {
      expect(sql).toContain('CREATE UNIQUE INDEX idx_performance_user_period');
    });
  });

  describe('Foreign keys', () => {
    test('users references auth.users with CASCADE', () => {
      expect(sql).toMatch(/users[\s\S]*?REFERENCES auth\.users\(id\) ON DELETE CASCADE/);
    });

    test('picks references users with CASCADE', () => {
      expect(sql).toMatch(/picks[\s\S]*?REFERENCES public\.users\(id\) ON DELETE CASCADE/);
    });

    test('outcomes references picks with CASCADE', () => {
      expect(sql).toMatch(/outcomes[\s\S]*?REFERENCES public\.picks\(id\) ON DELETE CASCADE/);
    });
  });

  describe('Triggers', () => {
    test('handle_new_user trigger exists', () => {
      expect(sql).toContain('CREATE OR REPLACE FUNCTION public.handle_new_user()');
      expect(sql).toContain('CREATE TRIGGER on_auth_user_created');
      expect(sql).toContain('AFTER INSERT ON auth.users');
    });

    test('updated_at trigger exists', () => {
      expect(sql).toContain('CREATE OR REPLACE FUNCTION public.update_updated_at()');
      expect(sql).toContain('CREATE TRIGGER users_updated_at');
      expect(sql).toContain('BEFORE UPDATE ON public.users');
    });

    test('scan count reset trigger exists', () => {
      expect(sql).toContain('CREATE OR REPLACE FUNCTION public.reset_scan_count()');
      expect(sql).toContain('CREATE TRIGGER users_scan_reset');
    });
  });

  describe('Timestamps', () => {
    test('all timestamp columns use TIMESTAMPTZ', () => {
      // Ensure no plain TIMESTAMP without TZ
      const timestampMatches = sql.match(/\bTIMESTAMP\b(?!TZ)/gi) || [];
      expect(timestampMatches.length).toBe(0);
    });
  });
});
