/**
 * Versioning & Rollback Test Suite
 * 
 * Tests the complete versioning system end-to-end including automatic
 * version creation, attribution, and rollback functionality.
 */

import { supabase } from '@/integrations/supabase/client';
import type { TestSuite, TestResult } from '../testRunner';
import { TestDataTracker } from '../TestDataTracker';

export const versioningTestSuite: TestSuite = {
  id: 'versioning',
  name: 'Versioning & Rollback',
  description: 'Tests version creation, attribution, rollback, and cleanup',
  tests: [
    {
      id: 'version-001',
      name: 'Automatic Version Creation on Insert',
      description: 'Verifies version 1 is created automatically when entity is created',
      run: async (): Promise<TestResult> => {
        const startTime = Date.now();
        const tracker = new TestDataTracker();
        let parkId: string | null = null;
        
        try {
          // Create a park
          const slug = `test-park-${Date.now()}`;
          const { data: park, error: createError } = await supabase
            .from('parks')
            .insert({
              name: 'Version Test Park',
              slug,
              park_type: 'theme_park',
              status: 'operating'
            })
            .select('id')
            .single();
          
          if (createError) throw new Error(`Park creation failed: ${createError.message}`);
          if (!park) throw new Error('No park returned from insert');
          
          parkId = park.id;
          
          // Poll for version creation
          let v1: any = null;
          const pollStart = Date.now();
          while (!v1 && Date.now() - pollStart < 5000) {
            const { data } = await supabase
              .from('park_versions')
              .select('version_id')
              .eq('park_id', park.id)
              .eq('version_number', 1)
              .single();
            
            if (data) {
              v1 = data;
              break;
            }
            await new Promise(resolve => setTimeout(resolve, 100));
          }
          
          // Check version was created
          const { data: version, error: versionError } = await supabase
            .from('park_versions')
            .select('*')
            .eq('park_id', park.id)
            .eq('version_number', 1)
            .single();
          
          if (versionError) throw new Error(`Version query failed: ${versionError.message}`);
          if (!version) throw new Error('Version 1 not created');
          if (version.name !== 'Version Test Park') throw new Error('Version has incorrect name');
          if (version.change_type !== 'created') throw new Error(`Expected change_type "created", got "${version.change_type}"`);
          if (!version.is_current) throw new Error('Version is not marked as current');
          
          const duration = Date.now() - startTime;
          
          return {
            id: 'version-001',
            name: 'Automatic Version Creation on Insert',
            suite: 'Versioning & Rollback',
            status: 'pass',
            duration,
            timestamp: new Date().toISOString(),
            details: {
              parkId: park.id,
              versionNumber: version.version_number,
              changeType: version.change_type,
              isCurrent: version.is_current
            }
          };
        } catch (error) {
          const duration = Date.now() - startTime;
          return {
            id: 'version-001',
            name: 'Automatic Version Creation on Insert',
            suite: 'Versioning & Rollback',
            status: 'fail',
            duration,
            error: error instanceof Error ? error.message : String(error),
            stack: error instanceof Error ? error.stack : undefined,
            timestamp: new Date().toISOString()
          };
        } finally {
          // Cleanup
          if (parkId) {
            await supabase.from('parks').delete().eq('id', parkId);
          }
        }
      }
    },
    {
      id: 'version-002',
      name: 'Automatic Version Creation on Update',
      description: 'Verifies version 2 is created when entity is updated',
      run: async (): Promise<TestResult> => {
        const startTime = Date.now();
        const tracker = new TestDataTracker();
        let parkId: string | null = null;
        
        try {
          // Create a park
          const slug = `test-park-${Date.now()}`;
          const { data: park, error: createError } = await supabase
            .from('parks')
            .insert({
              name: 'Original Name',
              slug,
              park_type: 'theme_park',
              status: 'operating'
            })
            .select('id')
            .single();
          
          if (createError) throw new Error(`Park creation failed: ${createError.message}`);
          if (!park) throw new Error('No park returned');
          
          parkId = park.id;
          
          // Wait for version 1
          await new Promise(resolve => setTimeout(resolve, 100));
          
          // Update the park
          const { error: updateError } = await supabase
            .from('parks')
            .update({ name: 'Updated Name' })
            .eq('id', park.id);
          
          if (updateError) throw new Error(`Park update failed: ${updateError.message}`);
          
          // Wait for version 2
          await new Promise(resolve => setTimeout(resolve, 100));
          
          // Check version 2 exists
          const { data: v2, error: v2Error } = await supabase
            .from('park_versions')
            .select('*')
            .eq('park_id', park.id)
            .eq('version_number', 2)
            .single();
          
          if (v2Error) throw new Error(`Version 2 query failed: ${v2Error.message}`);
          if (!v2) throw new Error('Version 2 not created');
          if (v2.name !== 'Updated Name') throw new Error('Version 2 has incorrect name');
          if (v2.change_type !== 'updated') throw new Error(`Expected change_type "updated", got "${v2.change_type}"`);
          if (!v2.is_current) throw new Error('Version 2 is not marked as current');
          
          // Check version 1 is no longer current
          const { data: v1, error: v1Error } = await supabase
            .from('park_versions')
            .select('is_current')
            .eq('park_id', park.id)
            .eq('version_number', 1)
            .single();
          
          if (v1Error) throw new Error(`Version 1 query failed: ${v1Error.message}`);
          if (v1?.is_current) throw new Error('Version 1 is still marked as current');
          
          const duration = Date.now() - startTime;
          
          return {
            id: 'version-002',
            name: 'Automatic Version Creation on Update',
            suite: 'Versioning & Rollback',
            status: 'pass',
            duration,
            timestamp: new Date().toISOString(),
            details: {
              parkId: park.id,
              v1IsCurrent: v1?.is_current,
              v2IsCurrent: v2.is_current,
              v2ChangeType: v2.change_type
            }
          };
        } catch (error) {
          const duration = Date.now() - startTime;
          return {
            id: 'version-002',
            name: 'Automatic Version Creation on Update',
            suite: 'Versioning & Rollback',
            status: 'fail',
            duration,
            error: error instanceof Error ? error.message : String(error),
            stack: error instanceof Error ? error.stack : undefined,
            timestamp: new Date().toISOString()
          };
        } finally {
          await tracker.cleanup();
          const remaining = await tracker.verifyCleanup();
          if (remaining.length > 0) {
            console.warn('version-001 cleanup incomplete:', remaining);
          }
        }
      }
    },
    {
      id: 'version-003',
      name: 'Rollback Authorization Check',
      description: 'Tests that rollback_to_version requires moderator role',
      run: async (): Promise<TestResult> => {
        const startTime = Date.now();
        const tracker = new TestDataTracker();
        let parkId: string | null = null;
        
        try {
          // Create a park
          const slug = `test-park-${Date.now()}`;
          const { data: park, error: createError } = await supabase
            .from('parks')
            .insert({
              name: 'Rollback Test Park',
              slug,
              park_type: 'theme_park',
              status: 'operating'
            })
            .select('id')
            .single();
          
          if (createError) throw new Error(`Park creation failed: ${createError.message}`);
          if (!park) throw new Error('No park returned');
          
          parkId = park.id;
          
          // Poll for version creation
          let v1: any = null;
          const pollStart = Date.now();
          while (!v1 && Date.now() - pollStart < 5000) {
            const { data } = await supabase
              .from('park_versions')
              .select('version_id')
              .eq('park_id', park.id)
              .eq('version_number', 1)
              .single();
            
            if (data) {
              v1 = data;
              break;
            }
            await new Promise(resolve => setTimeout(resolve, 100));
          }
          
          if (!v1) throw new Error('Version 1 not created after 5s timeout');
          
          // Check current user is moderator
          const { data: { user } } = await supabase.auth.getUser();
          if (!user) throw new Error('No authenticated user');
          
          const { data: isMod } = await supabase.rpc('is_moderator', { _user_id: user.id });
          
          // Try rollback
          const { error: rollbackError } = await supabase.rpc('rollback_to_version', {
            p_entity_type: 'park',
            p_entity_id: park.id,
            p_target_version_id: v1.version_id,
            p_changed_by: user.id,
            p_reason: 'Authorization test'
          });
          
          // If user is moderator, rollback should succeed
          // If not, rollback should fail with permission error
          if (isMod && rollbackError) {
            throw new Error(`Rollback failed for moderator: ${rollbackError.message}`);
          }
          
          if (!isMod && !rollbackError) {
            throw new Error('Rollback succeeded for non-moderator (should have failed)');
          }
          
          const duration = Date.now() - startTime;
          
          return {
            id: 'version-003',
            name: 'Rollback Authorization Check',
            suite: 'Versioning & Rollback',
            status: 'pass',
            duration,
            timestamp: new Date().toISOString(),
            details: {
              userIsModerator: isMod,
              rollbackBlocked: !isMod && !!rollbackError,
              authorizationEnforced: true
            }
          };
        } catch (error) {
          const duration = Date.now() - startTime;
          return {
            id: 'version-003',
            name: 'Rollback Authorization Check',
            suite: 'Versioning & Rollback',
            status: 'fail',
            duration,
            error: error instanceof Error ? error.message : String(error),
            stack: error instanceof Error ? error.stack : undefined,
            timestamp: new Date().toISOString()
          };
        } finally {
          await tracker.cleanup();
          const remaining = await tracker.verifyCleanup();
          if (remaining.length > 0) {
            console.warn('version-002 cleanup incomplete:', remaining);
          }
        }
      }
    },
    {
      id: 'version-004',
      name: 'Complete Rollback Flow',
      description: 'Tests end-to-end rollback with version 3 creation',
      run: async (): Promise<TestResult> => {
        const startTime = Date.now();
        const tracker = new TestDataTracker();
        let parkId: string | null = null;
        
        try {
          // Check if user is moderator
          const { data: { user } } = await supabase.auth.getUser();
          if (!user) throw new Error('No authenticated user');
          
          const { data: isMod } = await supabase.rpc('is_moderator', { _user_id: user.id });
          
          if (!isMod) {
            // Skip test if not moderator
            const duration = Date.now() - startTime;
            return {
              id: 'version-004',
              name: 'Complete Rollback Flow',
              suite: 'Versioning & Rollback',
              status: 'skip',
              duration,
              timestamp: new Date().toISOString(),
              details: { reason: 'User is not a moderator, test requires moderator role' }
            };
          }
          
          // Create park
          const slug = `test-park-${Date.now()}`;
          const { data: park, error: createError } = await supabase
            .from('parks')
            .insert({
              name: 'Original Name',
              slug,
              park_type: 'theme_park',
              status: 'operating',
              description: 'Original Description'
            })
            .select('id')
            .single();
          
          if (createError) throw new Error(`Park creation failed: ${createError.message}`);
          if (!park) throw new Error('No park returned');
          
          parkId = park.id;
          await new Promise(resolve => setTimeout(resolve, 100));
          
          // Get version 1
          const { data: v1, error: v1Error } = await supabase
            .from('park_versions')
            .select('version_id, name, description')
            .eq('park_id', park.id)
            .eq('version_number', 1)
            .single();
          
          if (v1Error || !v1) throw new Error('Version 1 not found');
          
          // Update park
          const { error: updateError } = await supabase
            .from('parks')
            .update({ name: 'Modified Name', description: 'Modified Description' })
            .eq('id', park.id);
          
          if (updateError) throw new Error(`Park update failed: ${updateError.message}`);
          
          await new Promise(resolve => setTimeout(resolve, 100));
          
          // Verify version 2
          const { data: v2 } = await supabase
            .from('park_versions')
            .select('version_number, name')
            .eq('park_id', park.id)
            .eq('version_number', 2)
            .single();
          
          if (!v2) throw new Error('Version 2 not created');
          if (v2.name !== 'Modified Name') throw new Error('Version 2 has incorrect data');
          
          // Rollback to version 1
          const { error: rollbackError } = await supabase.rpc('rollback_to_version', {
            p_entity_type: 'park',
            p_entity_id: park.id,
            p_target_version_id: v1.version_id,
            p_changed_by: user.id,
            p_reason: 'Integration test rollback'
          });
          
          if (rollbackError) throw new Error(`Rollback failed: ${rollbackError.message}`);
          
          await new Promise(resolve => setTimeout(resolve, 200));
          
          // Verify park data restored
          const { data: restored, error: restoredError } = await supabase
            .from('parks')
            .select('name, description')
            .eq('id', park.id)
            .single();
          
          if (restoredError) throw new Error(`Failed to fetch restored park: ${restoredError.message}`);
          if (!restored) throw new Error('Restored park not found');
          if (restored.name !== 'Original Name') {
            throw new Error(`Rollback failed: expected "Original Name", got "${restored.name}"`);
          }
          if (restored.description !== 'Original Description') {
            throw new Error(`Description not restored: expected "Original Description", got "${restored.description}"`);
          }
          
          // Verify version 3 created with change_type = 'restored'
          const { data: v3, error: v3Error } = await supabase
            .from('park_versions')
            .select('*')
            .eq('park_id', park.id)
            .eq('version_number', 3)
            .single();
          
          if (v3Error || !v3) throw new Error('Version 3 (restored) not created');
          if (v3.change_type !== 'restored') {
            throw new Error(`Expected change_type "restored", got "${v3.change_type}"`);
          }
          if (v3.name !== 'Original Name') throw new Error('Version 3 has incorrect data');
          if (!v3.is_current) throw new Error('Version 3 is not marked as current');
          
          const duration = Date.now() - startTime;
          
          return {
            id: 'version-004',
            name: 'Complete Rollback Flow',
            suite: 'Versioning & Rollback',
            status: 'pass',
            duration,
            timestamp: new Date().toISOString(),
            details: {
              parkId: park.id,
              versionsCreated: 3,
              dataRestored: true,
              v3ChangeType: v3.change_type,
              v3IsCurrent: v3.is_current
            }
          };
        } catch (error) {
          const duration = Date.now() - startTime;
          return {
            id: 'version-004',
            name: 'Complete Rollback Flow',
            suite: 'Versioning & Rollback',
            status: 'fail',
            duration,
            error: error instanceof Error ? error.message : String(error),
            stack: error instanceof Error ? error.stack : undefined,
            timestamp: new Date().toISOString()
          };
        } finally {
          await tracker.cleanup();
          const remaining = await tracker.verifyCleanup();
          if (remaining.length > 0) {
            console.warn('version-003 cleanup incomplete:', remaining);
          }
        }
      }
    }
  ]
};
