/**
 * Versioning & Rollback Test Suite
 * 
 * Tests the complete versioning system end-to-end including automatic
 * version creation, attribution, and rollback functionality.
 * 
 * All tests follow the sacred pipeline: submitParkCreation → approve → verify versioning
 */

import { supabase } from '@/lib/supabaseClient';
import type { TestSuite, TestResult } from '../testRunner';
import { TestDataTracker } from '../TestDataTracker';
import { formatTestError } from '../formatTestError';
import {
  generateUniqueParkData,
  createTestParkSubmission,
  approveSubmission,
  pollForEntity,
  pollForVersion,
  getAuthToken,
  getCurrentUserId,
} from '../helpers/approvalTestHelpers';

export const versioningTestSuite: TestSuite = {
  id: 'versioning',
  name: 'Versioning & Rollback',
  description: 'Tests version creation, attribution, rollback, and cleanup via sacred pipeline',
  tests: [
    {
      id: 'version-001',
      name: 'Automatic Version Creation on Insert',
      description: 'Verifies version 1 is created automatically when entity is approved',
      run: async (): Promise<TestResult> => {
        const startTime = Date.now();
        const tracker = new TestDataTracker();
        
        try {
          // Follow sacred pipeline: Form → Submission → Approval → Versioning
          const userId = await getCurrentUserId();
          const authToken = await getAuthToken();
          const parkData = generateUniqueParkData('version-001');
          
          // Create submission
          const { submissionId, itemId } = await createTestParkSubmission(parkData, userId, tracker);
          
          // Approve submission
          const approval = await approveSubmission(submissionId, [itemId], authToken);
          if (!approval.success) {
            throw new Error(`Approval failed: ${approval.error}`);
          }
          
          // Get approved entity ID
          const { data: item } = await supabase
            .from('submission_items')
            .select('approved_entity_id')
            .eq('id', itemId)
            .single();
          
          if (!item?.approved_entity_id) {
            throw new Error('No entity ID returned after approval');
          }
          
          const parkId = item.approved_entity_id;
          tracker.track('parks', parkId);
          
          // Poll for park entity
          const park = await pollForEntity('parks', parkId);
          if (!park) throw new Error('Park not created after approval');
          
          // Verify version 1 was created automatically
          const version = await pollForVersion('park', parkId, 1);
          if (!version) throw new Error('Version 1 not created');
          
          if (version.name !== parkData.name) {
            throw new Error(`Version has incorrect name: expected "${parkData.name}", got "${version.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,
              submissionId,
              versionNumber: version.version_number,
              changeType: version.change_type,
              isCurrent: version.is_current,
              followedPipeline: true
            }
          };
        } catch (error) {
          const duration = Date.now() - startTime;
          return {
            id: 'version-001',
            name: 'Automatic Version Creation on Insert',
            suite: 'Versioning & Rollback',
            status: 'fail',
            duration,
            error: formatTestError(error),
            stack: error instanceof Error ? error.stack : undefined,
            timestamp: new Date().toISOString()
          };
        } finally {
          await tracker.cleanup();
        }
      }
    },
    {
      id: 'version-002',
      name: 'Automatic Version Creation on Update',
      description: 'Verifies version 2 is created when entity is updated via pipeline',
      run: async (): Promise<TestResult> => {
        const startTime = Date.now();
        const tracker = new TestDataTracker();
        
        try {
          // Create and approve initial park
          const userId = await getCurrentUserId();
          const authToken = await getAuthToken();
          const parkData = generateUniqueParkData('version-002');
          
          const { submissionId, itemId } = await createTestParkSubmission(parkData, userId, tracker);
          const approval = await approveSubmission(submissionId, [itemId], authToken);
          
          if (!approval.success) {
            throw new Error(`Initial approval failed: ${approval.error}`);
          }
          
          // Get park ID
          const { data: item } = await supabase
            .from('submission_items')
            .select('approved_entity_id')
            .eq('id', itemId)
            .single();
          
          const parkId = item?.approved_entity_id;
          if (!parkId) throw new Error('No park ID after approval');
          
          tracker.track('parks', parkId);
          
          // Wait for version 1
          const v1 = await pollForVersion('park', parkId, 1);
          if (!v1) throw new Error('Version 1 not created');
          
          // Update park directly (simulating approved edit)
          // In production, this would go through edit submission pipeline
          const { error: updateError } = await supabase
            .from('parks')
            .update({ name: 'Updated Name', description: 'Updated Description' })
            .eq('id', parkId);
          
          if (updateError) throw new Error(`Park update failed: ${updateError.message}`);
          
          // Verify version 2 created
          const v2 = await pollForVersion('park', parkId, 2);
          if (!v2) throw new Error('Version 2 not created after update');
          
          if (v2.name !== 'Updated Name') {
            throw new Error(`Version 2 has incorrect name: expected "Updated Name", got "${v2.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');
          }
          
          // Verify version 1 is no longer current
          const { data: v1Updated } = await supabase
            .from('park_versions')
            .select('is_current')
            .eq('park_id', parkId)
            .eq('version_number', 1)
            .single();
          
          if (v1Updated?.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,
              v1IsCurrent: v1Updated?.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: formatTestError(error),
            stack: error instanceof Error ? error.stack : undefined,
            timestamp: new Date().toISOString()
          };
        } finally {
          await tracker.cleanup();
        }
      }
    },
    {
      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();
        
        try {
          // Create and approve park
          const userId = await getCurrentUserId();
          const authToken = await getAuthToken();
          const parkData = generateUniqueParkData('version-003');
          
          const { submissionId, itemId } = await createTestParkSubmission(parkData, userId, tracker);
          const approval = await approveSubmission(submissionId, [itemId], authToken);
          
          if (!approval.success) {
            throw new Error(`Approval failed: ${approval.error}`);
          }
          
          // Get park ID
          const { data: item } = await supabase
            .from('submission_items')
            .select('approved_entity_id')
            .eq('id', itemId)
            .single();
          
          const parkId = item?.approved_entity_id;
          if (!parkId) throw new Error('No park ID after approval');
          
          tracker.track('parks', parkId);
          
          // Wait for version 1
          const v1 = await pollForVersion('park', parkId, 1);
          if (!v1) throw new Error('Version 1 not created');
          
          // Check current user role
          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: parkId,
            p_target_version_id: v1.version_id,
            p_changed_by: user.id,
            p_reason: 'Authorization test'
          });
          
          // Verify authorization enforcement
          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: formatTestError(error),
            stack: error instanceof Error ? error.stack : undefined,
            timestamp: new Date().toISOString()
          };
        } finally {
          await tracker.cleanup();
        }
      }
    },
    {
      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();
        
        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) {
            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 and approve park
          const userId = await getCurrentUserId();
          const authToken = await getAuthToken();
          const parkData = {
            ...generateUniqueParkData('version-004'),
            description: 'Original Description'
          };
          
          const { submissionId, itemId } = await createTestParkSubmission(parkData, userId, tracker);
          const approval = await approveSubmission(submissionId, [itemId], authToken);
          
          if (!approval.success) {
            throw new Error(`Approval failed: ${approval.error}`);
          }
          
          // Get park ID
          const { data: item } = await supabase
            .from('submission_items')
            .select('approved_entity_id')
            .eq('id', itemId)
            .single();
          
          const parkId = item?.approved_entity_id;
          if (!parkId) throw new Error('No park ID after approval');
          
          tracker.track('parks', parkId);
          
          // Wait for version 1
          const v1 = await pollForVersion('park', parkId, 1);
          if (!v1) throw new Error('Version 1 not created');
          
          // Update park
          const { error: updateError } = await supabase
            .from('parks')
            .update({ name: 'Modified Name', description: 'Modified Description' })
            .eq('id', parkId);
          
          if (updateError) throw new Error(`Park update failed: ${updateError.message}`);
          
          // Wait for version 2
          const v2 = await pollForVersion('park', parkId, 2);
          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: parkId,
            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}`);
          
          // Verify park data restored
          const restored = await pollForEntity('parks', parkId, 3000);
          if (!restored) throw new Error('Could not fetch restored park');
          
          if (restored.name !== parkData.name) {
            throw new Error(`Rollback failed: expected "${parkData.name}", got "${restored.name}"`);
          }
          if (restored.description !== 'Original Description') {
            throw new Error(`Description not restored: got "${restored.description}"`);
          }
          
          // Verify version 3 created with change_type = 'restored'
          const v3 = await pollForVersion('park', parkId, 3, 3000);
          if (!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 !== parkData.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,
              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: formatTestError(error),
            stack: error instanceof Error ? error.stack : undefined,
            timestamp: new Date().toISOString()
          };
        } finally {
          await tracker.cleanup();
        }
      }
    }
  ]
};
