/**
 * Supabase Service
 * Handles all database operations for submissions and userslist
 */

import { createClient, SupabaseClient } from '@supabase/supabase-js';
import { config } from '../config';
import { logger } from '../utils/logger';
import type { SubmissionRecord, SubmissionInput, SubmissionResult } from '../types';

class SupabaseService {
  private static instance: SupabaseService;
  private client: SupabaseClient;

  private constructor() {
    this.client = createClient(config.supabaseUrl, config.supabaseServiceRoleKey, {
      auth: {
        autoRefreshToken: false,
        persistSession: false,
      },
    });
    logger.debug('✅ Supabase client initialized');
  }

  public static getInstance(): SupabaseService {
    if (!SupabaseService.instance) {
      SupabaseService.instance = new SupabaseService();
    }
    return SupabaseService.instance;
  }

  /**
   * Create new submission
   */
  public async createSubmission(
    submissionId: string,
    input: SubmissionInput
  ): Promise<SubmissionRecord> {
    try {
      logger.debug('Creating submission', { submissionId });

      const { data, error } = await this.client
        .from('submissions')
        .insert({
          submission_id: submissionId,
          submission_type: 'tms_permission',
          status: 'pending',
          input: input,
          results: [],
          created_at: new Date().toISOString(),
          updated_at: new Date().toISOString(),
        })
        .select()
        .single();

      if (error) {
        throw error;
      }

      // Add usernames to userslist for autocomplete (batch upsert)
      await this.addUsernames(input.username_list);

      return data as SubmissionRecord;
    } catch (error: any) {
      logger.error('Error creating submission', {
        submissionId,
        error: error.message,
      });
      throw error;
    }
  }

  /**
   * Get pending submissions (for worker)
   */
  public async getPendingSubmissions(): Promise<SubmissionRecord[]> {
    try {
      const { data, error } = await this.client
        .from('submissions')
        .select('*')
        .eq('submission_type', 'tms_permission')
        .eq('status', 'pending')
        .order('created_at', { ascending: true })
        .limit(10);

      if (error) {
        throw error;
      }

      return (data || []) as SubmissionRecord[];
    } catch (error: any) {
      logger.error('Error getting pending submissions', {
        error: error.message,
      });
      return [];
    }
  }

  /**
   * Get processing submissions (for stuck submission detection)
   */
  public async getProcessingSubmissions(): Promise<SubmissionRecord[]> {
    try {
      const { data, error } = await this.client
        .from('submissions')
        .select('*')
        .eq('submission_type', 'tms_permission')
        .eq('status', 'processing')
        .order('created_at', { ascending: true });

      if (error) {
        throw error;
      }

      return (data || []) as SubmissionRecord[];
    } catch (error: any) {
      logger.error('Error getting processing submissions', {
        error: error.message,
      });
      return [];
    }
  }

  /**
   * Update submission status and results
   */
  public async updateSubmissionStatus(
    submissionId: string,
    status: 'pending' | 'processing' | 'completed' | 'failed',
    results?: SubmissionResult[],
    errorMessage?: string
  ): Promise<void> {
    try {
      const updateData: any = {
        status,
        updated_at: new Date().toISOString(),
      };

      if (results) {
        updateData.results = results;
      }

      if (errorMessage) {
        updateData.error_message = errorMessage;
      }

      const { error } = await this.client
        .from('submissions')
        .update(updateData)
        .eq('submission_id', submissionId);

      if (error) {
        throw error;
      }

      logger.debug('Submission status updated', { submissionId, status });
    } catch (error: any) {
      logger.error('Error updating submission status', {
        submissionId,
        status,
        error: error.message,
      });
      throw error;
    }
  }

  /**
   * Get submission by ID
   */
  public async getSubmission(submissionId: string): Promise<SubmissionRecord | null> {
    try {
      const { data, error } = await this.client
        .from('submissions')
        .select('*')
        .eq('submission_id', submissionId)
        .single();

      if (error) {
        if (error.code === 'PGRST116') {
          // Not found
          return null;
        }
        throw error;
      }

      return data as SubmissionRecord;
    } catch (error: any) {
      logger.error('Error getting submission', {
        submissionId,
        error: error.message,
      });
      throw error;
    }
  }

  /**
   * Get submissions history
   */
  public async getSubmissionsHistory(limit: number = 30): Promise<SubmissionRecord[]> {
    try {
      const { data, error } = await this.client
        .from('submissions')
        .select('*')
        .eq('submission_type', 'tms_permission')
        .order('created_at', { ascending: false })
        .limit(limit);

      if (error) {
        throw error;
      }

      return (data || []) as SubmissionRecord[];
    } catch (error: any) {
      logger.error('Error getting submissions history', {
        error: error.message,
      });
      throw error;
    }
  }

  /**
   * Delete submission
   */
  public async deleteSubmission(submissionId: string): Promise<void> {
    try {
      const { error } = await this.client
        .from('submissions')
        .delete()
        .eq('submission_id', submissionId)
        .eq('submission_type', 'tms_permission');

      if (error) {
        throw error;
      }

      logger.debug('Deleted submission', { submissionId });
    } catch (error: any) {
      logger.error('Error deleting submission', {
        submissionId,
        error: error.message,
      });
      throw error;
    }
  }

  /**
   * Retry submission (reset to pending)
   */
  public async retrySubmission(submissionId: string): Promise<SubmissionRecord> {
    try {
      const { data, error } = await this.client
        .from('submissions')
        .update({
          status: 'pending',
          results: [],
          error_message: null,
          updated_at: new Date().toISOString(),
        })
        .eq('submission_id', submissionId)
        .eq('submission_type', 'tms_permission')
        .select()
        .single();

      if (error) {
        throw error;
      }

      logger.debug('Submission reset to pending for retry', { submissionId });
      return data as SubmissionRecord;
    } catch (error: any) {
      logger.error('Error retrying submission', {
        submissionId,
        error: error.message,
      });
      throw error;
    }
  }

  /**
   * Create a new submission for retry with only error GE IDs and usernames
   */
  public async createRetrySubmission(
    errorUsernames: string[],
    errorGeInput: string
  ): Promise<SubmissionRecord> {
    try {
      const submissionId = `retry-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;

      logger.debug('Creating retry submission', { submissionId, usernames: errorUsernames });

      const input: SubmissionInput = {
        submission_type: 'tms_permission',
        username_list: errorUsernames,
        ge_input: errorGeInput,
      };

      const { data, error } = await this.client
        .from('submissions')
        .insert({
          submission_id: submissionId,
          submission_type: 'tms_permission',
          status: 'pending',
          input: input,
          results: [],
          created_at: new Date().toISOString(),
          updated_at: new Date().toISOString(),
        })
        .select()
        .single();

      if (error) {
        throw error;
      }

      logger.debug('Retry submission created', { submissionId });
      return data as SubmissionRecord;
    } catch (error: any) {
      logger.error('Error creating retry submission', {
        error: error.message,
      });
      throw error;
    }
  }

  /**
   * Add username to userslist (for autocomplete)
   */
  public async addUsername(username: string): Promise<void> {
    try {
      // Upsert (insert or update)
      const { error } = await this.client.from('userslist').upsert(
        {
          username: username,
          updated_at: new Date().toISOString(),
        },
        {
          onConflict: 'username',
        }
      );

      if (error) {
        logger.warn('Error adding username to userslist', {
          username,
          error: error.message,
        });
      }
    } catch (error: any) {
      logger.warn('Error adding username to userslist', {
        username,
        error: error.message,
      });
    }
  }

  /**
   * Add multiple usernames to userslist (batch upsert for performance)
   */
  public async addUsernames(usernames: string[]): Promise<void> {
    if (usernames.length === 0) return;
    
    try {
      const now = new Date().toISOString();
      const records = usernames.map(username => ({
        username,
        updated_at: now,
      }));

      const { error } = await this.client.from('userslist').upsert(
        records,
        { onConflict: 'username' }
      );

      if (error) {
        logger.warn('Error batch adding usernames to userslist', {
          count: usernames.length,
          error: error.message,
        });
      }
    } catch (error: any) {
      logger.warn('Error batch adding usernames to userslist', {
        count: usernames.length,
        error: error.message,
      });
    }
  }

  /**
   * Get all usernames (for autocomplete)
   */
  public async getAllUsernames(): Promise<string[]> {
    try {
      const { data, error } = await this.client
        .from('userslist')
        .select('username')
        .order('updated_at', { ascending: false });

      if (error) {
        throw error;
      }

      return (data || []).map((row: any) => row.username);
    } catch (error: any) {
      logger.error('Error getting usernames', { error: error.message });
      return [];
    }
  }

  // ==================== Check List Methods ====================

  public async createCheck(input: any): Promise<any> {
    try {
      const { data, error } = await this.client
        .from('check_list')
        .insert({
          input: input,
          status: 'pending',
          results: [],
        })
        .select()
        .single();

      if (error) throw error;
      return data;
    } catch (error: any) {
      logger.error('Error creating check', { error: error.message });
      throw error;
    }
  }

  public async getPendingChecks(): Promise<any[]> {
    try {
      const { data, error } = await this.client
        .from('check_list')
        .select('*')
        .eq('status', 'pending')
        .order('created_at', { ascending: true });

      if (error) throw error;
      return data || [];
    } catch (error: any) {
      logger.error('Error getting pending checks', { error: error.message });
      return [];
    }
  }

  public async updateCheckStatus(
    id: string,
    status: string,
    results?: any[],
    errorMessage?: string
  ): Promise<void> {
    try {
      const updateData: any = { status };
      if (results) updateData.results = results;
      if (errorMessage) updateData.error = errorMessage;

      const { error } = await this.client
        .from('check_list')
        .update(updateData)
        .eq('id', id);

      if (error) throw error;
    } catch (error: any) {
      logger.error('Error updating check status', { id, error: error.message });
      throw error;
    }
  }

  public async getCheckHistory(limit: number = 30): Promise<any[]> {
    try {
      const { data, error } = await this.client
        .from('check_list')
        .select('*')
        .order('created_at', { ascending: false })
        .limit(limit);

      if (error) throw error;
      return data || [];
    } catch (error: any) {
      logger.error('Error getting check history', { error: error.message });
      throw error;
    }
  }

  public async deleteCheck(id: string): Promise<void> {
    try {
      const { error } = await this.client
        .from('check_list')
        .delete()
        .eq('id', id);

      if (error) throw error;
    } catch (error: any) {
      logger.error('Error deleting check record', { id, error: error.message });
      throw error;
    }
  }

  // ============= Session Management =============

  /**
   * Save TMS session to Supabase
   */
  public async saveTmsSession(cookies: string[], lastLogin: number): Promise<void> {
    try {
      const { error } = await this.client
        .from('sessions')
        .upsert({
          id: 'tms',
          data: { cookies },
          last_login: new Date(lastLogin).toISOString(),
          updated_at: new Date().toISOString(),
        });

      if (error) throw error;
      logger.debug('TMS session saved to Supabase');
    } catch (error: any) {
      logger.warn('Failed to save TMS session to Supabase', { error: error.message });
    }
  }

  /**
   * Load TMS session from Supabase
   */
  public async loadTmsSession(): Promise<{ cookies: string[]; lastLogin: number } | null> {
    try {
      const { data, error } = await this.client
        .from('sessions')
        .select('data, last_login')
        .eq('id', 'tms')
        .single();

      if (error) throw error;

      if (data && data.data?.cookies) {
        return {
          cookies: data.data.cookies,
          lastLogin: new Date(data.last_login).getTime(),
        };
      }
      return null;
    } catch (error: any) {
      logger.warn('Failed to load TMS session from Supabase', { error: error.message });
      return null;
    }
  }

  /**
   * Delete TMS session from Supabase
   */
  public async deleteTmsSession(): Promise<void> {
    try {
      const { error } = await this.client
        .from('sessions')
        .update({
          data: { cookies: [] },
          last_login: null,
          updated_at: new Date().toISOString(),
        })
        .eq('id', 'tms');

      if (error) throw error;
      logger.debug('TMS session cleared from Supabase');
    } catch (error: any) {
      logger.warn('Failed to clear TMS session from Supabase', { error: error.message });
    }
  }
}

export const supabaseService = SupabaseService.getInstance();
