#!/usr/bin/env tsx
/**
 * Schema Validation Script
 * 
 * Pre-migration validation script that checks schema consistency
 * across the submission pipeline before deploying changes.
 * 
 * Usage:
 *   npm run validate-schema
 *   or
 *   tsx scripts/validate-schema.ts
 * 
 * Exit codes:
 *   0 = All validations passed
 *   1 = Validation failures detected
 */

import { createClient } from '@supabase/supabase-js';

const SUPABASE_URL = 'https://ydvtmnrszybqnbcqbdcy.supabase.co';
const SUPABASE_KEY = process.env.SUPABASE_SERVICE_ROLE_KEY;

if (!SUPABASE_KEY) {
  console.error('❌ SUPABASE_SERVICE_ROLE_KEY environment variable is required');
  process.exit(1);
}

const supabase = createClient(SUPABASE_URL, SUPABASE_KEY);

interface ValidationResult {
  category: string;
  test: string;
  passed: boolean;
  message?: string;
}

const results: ValidationResult[] = [];

async function getTableColumns(tableName: string): Promise<Set<string>> {
  const { data, error } = await supabase
    .from('information_schema.columns' as any)
    .select('column_name')
    .eq('table_schema', 'public')
    .eq('table_name', tableName);

  if (error) throw error;

  return new Set(data?.map((row: any) => row.column_name) || []);
}

async function validateSubmissionTable(
  mainTable: string,
  submissionTable: string,
  entityName: string
): Promise<void> {
  const mainColumns = await getTableColumns(mainTable);
  const submissionColumns = await getTableColumns(submissionTable);

  const excludedFields = new Set([
    'id', 'created_at', 'updated_at', 'is_test_data',
    'view_count_all', 'view_count_30d', 'view_count_7d',
    'average_rating', 'review_count', 'installations_count',
  ]);

  const missingFields: string[] = [];

  for (const field of mainColumns) {
    if (excludedFields.has(field)) continue;
    if (!submissionColumns.has(field)) {
      missingFields.push(field);
    }
  }

  if (missingFields.length === 0) {
    results.push({
      category: 'Submission Tables',
      test: `${entityName}: submission table matches main table`,
      passed: true,
    });
  } else {
    results.push({
      category: 'Submission Tables',
      test: `${entityName}: submission table matches main table`,
      passed: false,
      message: `Missing fields: ${missingFields.join(', ')}`,
    });
  }
}

async function validateVersionTable(
  mainTable: string,
  versionTable: string,
  entityName: string
): Promise<void> {
  const mainColumns = await getTableColumns(mainTable);
  const versionColumns = await getTableColumns(versionTable);

  const excludedFields = new Set([
    'id', 'created_at', 'updated_at', 'is_test_data',
    'view_count_all', 'view_count_30d', 'view_count_7d',
    'average_rating', 'review_count', 'installations_count',
  ]);

  const fieldMapping: { [key: string]: string } = {
    'height_requirement': 'height_requirement_cm',
    'max_g_force': 'gforce_max',
    'inversions': 'inversions_count',
    'max_height_meters': 'height_meters',
    'drop_height_meters': 'drop_meters',
  };

  const requiredVersionFields = new Set([
    'version_id', 'version_number', 'change_type', 'change_reason',
    'is_current', 'created_by', 'submission_id', 'is_test_data',
  ]);

  const missingMainFields: string[] = [];
  const missingVersionFields: string[] = [];

  // Check main table fields exist in version table
  for (const field of mainColumns) {
    if (excludedFields.has(field)) continue;
    
    const mappedField = fieldMapping[field] || field;
    if (!versionColumns.has(field) && !versionColumns.has(mappedField)) {
      missingMainFields.push(field);
    }
  }

  // Check version metadata fields exist
  for (const field of requiredVersionFields) {
    if (!versionColumns.has(field)) {
      missingVersionFields.push(field);
    }
  }

  if (missingMainFields.length === 0 && missingVersionFields.length === 0) {
    results.push({
      category: 'Version Tables',
      test: `${entityName}: version table has all fields`,
      passed: true,
    });
  } else {
    const messages: string[] = [];
    if (missingMainFields.length > 0) {
      messages.push(`Missing main fields: ${missingMainFields.join(', ')}`);
    }
    if (missingVersionFields.length > 0) {
      messages.push(`Missing version fields: ${missingVersionFields.join(', ')}`);
    }

    results.push({
      category: 'Version Tables',
      test: `${entityName}: version table has all fields`,
      passed: false,
      message: messages.join('; '),
    });
  }
}

async function validateCriticalFields(): Promise<void> {
  const ridesColumns = await getTableColumns('rides');
  const rideModelsColumns = await getTableColumns('ride_models');

  // Rides should NOT have ride_type
  if (!ridesColumns.has('ride_type')) {
    results.push({
      category: 'Critical Fields',
      test: 'rides table does NOT have ride_type column',
      passed: true,
    });
  } else {
    results.push({
      category: 'Critical Fields',
      test: 'rides table does NOT have ride_type column',
      passed: false,
      message: 'rides table incorrectly has ride_type column',
    });
  }

  // Rides MUST have category
  if (ridesColumns.has('category')) {
    results.push({
      category: 'Critical Fields',
      test: 'rides table has category column',
      passed: true,
    });
  } else {
    results.push({
      category: 'Critical Fields',
      test: 'rides table has category column',
      passed: false,
      message: 'rides table is missing required category column',
    });
  }

  // Ride models must have both category and ride_type
  if (rideModelsColumns.has('category') && rideModelsColumns.has('ride_type')) {
    results.push({
      category: 'Critical Fields',
      test: 'ride_models has both category and ride_type',
      passed: true,
    });
  } else {
    const missing: string[] = [];
    if (!rideModelsColumns.has('category')) missing.push('category');
    if (!rideModelsColumns.has('ride_type')) missing.push('ride_type');

    results.push({
      category: 'Critical Fields',
      test: 'ride_models has both category and ride_type',
      passed: false,
      message: `ride_models is missing: ${missing.join(', ')}`,
    });
  }
}

async function validateFunctions(): Promise<void> {
  const functionsToCheck = [
    'create_entity_from_submission',
    'update_entity_from_submission',
    'process_approval_transaction',
  ];

  for (const funcName of functionsToCheck) {
    try {
      const { data, error } = await supabase
        .rpc('pg_catalog.pg_function_is_visible' as any, {
          funcid: `public.${funcName}`::any
        } as any);

      if (!error) {
        results.push({
          category: 'Functions',
          test: `${funcName} exists and is accessible`,
          passed: true,
        });
      } else {
        results.push({
          category: 'Functions',
          test: `${funcName} exists and is accessible`,
          passed: false,
          message: error.message,
        });
      }
    } catch (err) {
      results.push({
        category: 'Functions',
        test: `${funcName} exists and is accessible`,
        passed: false,
        message: err instanceof Error ? err.message : String(err),
      });
    }
  }
}

function printResults(): void {
  console.log('\n' + '='.repeat(80));
  console.log('Schema Validation Results');
  console.log('='.repeat(80) + '\n');

  const categories = [...new Set(results.map(r => r.category))];
  let totalPassed = 0;
  let totalFailed = 0;

  for (const category of categories) {
    const categoryResults = results.filter(r => r.category === category);
    const passed = categoryResults.filter(r => r.passed).length;
    const failed = categoryResults.filter(r => !r.passed).length;

    console.log(`\n${category}:`);
    console.log('-'.repeat(80));

    for (const result of categoryResults) {
      const icon = result.passed ? '✅' : '❌';
      console.log(`${icon} ${result.test}`);
      if (result.message) {
        console.log(`   └─ ${result.message}`);
      }
    }

    totalPassed += passed;
    totalFailed += failed;
  }

  console.log('\n' + '='.repeat(80));
  console.log(`Total: ${totalPassed} passed, ${totalFailed} failed`);
  console.log('='.repeat(80) + '\n');
}

async function main(): Promise<void> {
  console.log('🔍 Starting schema validation...\n');

  try {
    // Validate submission tables
    await validateSubmissionTable('parks', 'park_submissions', 'Parks');
    await validateSubmissionTable('rides', 'ride_submissions', 'Rides');
    await validateSubmissionTable('companies', 'company_submissions', 'Companies');
    await validateSubmissionTable('ride_models', 'ride_model_submissions', 'Ride Models');

    // Validate version tables
    await validateVersionTable('parks', 'park_versions', 'Parks');
    await validateVersionTable('rides', 'ride_versions', 'Rides');
    await validateVersionTable('companies', 'company_versions', 'Companies');
    await validateVersionTable('ride_models', 'ride_model_versions', 'Ride Models');

    // Validate critical fields
    await validateCriticalFields();

    // Validate functions
    await validateFunctions();

    // Print results
    printResults();

    // Exit with appropriate code
    const hasFailures = results.some(r => !r.passed);
    if (hasFailures) {
      console.error('❌ Schema validation failed. Please fix the issues above before deploying.\n');
      process.exit(1);
    } else {
      console.log('✅ All schema validations passed. Safe to deploy.\n');
      process.exit(0);
    }
  } catch (error) {
    console.error('❌ Fatal error during validation:');
    console.error(error);
    process.exit(1);
  }
}

main();
