/**
 * TMS Permission Backend Server
 * Express API with automated permission logic
 */

import express, { Request, Response } from 'express';
import cors from 'cors';
import { config } from './config'; // Must be imported first to load env vars
import { mongodbService } from './services/mongodb.service';
import { supabaseService } from './services/supabase.service';
import { tmsApiService } from './services/tms-api.service';
import { startWorker, stopWorker } from './services/worker.service';
import { parseGeInput } from './controllers/permission.controller';
import * as checkController from './controllers/check.controller';
import { logger } from './utils/logger';
import type {
  SubmissionInput,
  ApiResponse,
  SubmitResponse,
  StatusResponse,
  SubmissionRecord,
} from './types';

const app = express();
const PORT = config.port;

// Middleware
app.use(cors());
app.use(express.json());

/**
 * POST /api/submit
 * Create new TMS permission submission
 */
app.post('/api/submit', async (req: Request, res: Response) => {
  try {
    const input: SubmissionInput = req.body;

    // Validate input
    if (input.submission_type !== 'tms_permission') {
      return res.status(400).json({
        success: false,
        error: 'Invalid submission_type (must be "tms_permission")',
      } as ApiResponse);
    }

    if (!input.username_list || input.username_list.length === 0) {
      return res.status(400).json({
        success: false,
        error: 'username_list is required and must not be empty',
      } as ApiResponse);
    }

    if (!input.ge_input || input.ge_input.trim().length === 0) {
      return res.status(400).json({
        success: false,
        error: 'ge_input is required',
      } as ApiResponse);
    }

    // Parse and validate ge_input
    const parsedGes = parseGeInput(input.ge_input);
    if (parsedGes.length === 0) {
      return res.status(400).json({
        success: false,
        error: 'ge_input contains no valid entries',
      } as ApiResponse);
    }

    // Generate submission ID
    const submissionId = `tms_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;

    // Create submission in database
    await supabaseService.createSubmission(submissionId, input);

    logger.debug('✅ Submission created', {
      submissionId,
      usernames: input.username_list.length,
      ges: parsedGes.length,
    });

    const response: SubmitResponse = {
      submission_id: submissionId,
      status: 'pending',
      message: `Đã tạo yêu cầu cấp quyền cho ${input.username_list.length} user(s) × ${parsedGes.length} GE(s)`,
    };

    res.json({
      success: true,
      data: response,
    } as ApiResponse<SubmitResponse>);
  } catch (error: any) {
    logger.error('Error in /api/submit', { error: error.message });
    res.status(500).json({
      success: false,
      error: error.message,
    } as ApiResponse);
  }
});

/**
 * GET /api/submissions/:id
 * Get submission status and results
 */
app.get('/api/submissions/:id', async (req: Request, res: Response) => {
  try {
    const submissionId = req.params.id;

    const submission = await supabaseService.getSubmission(submissionId);

    if (!submission) {
      return res.status(404).json({
        success: false,
        error: 'Submission not found',
      } as ApiResponse);
    }

    const results = submission.results || [];
    const total = results.length;
    const completed = results.filter(r => r.status === 'success').length;
    const failed = results.filter(r => r.status === 'error').length;

    const response: StatusResponse = {
      submission_id: submission.submission_id,
      status: submission.status,
      progress: {
        total,
        completed,
        failed,
      },
      results,
      usernames: submission.input?.username_list || [],
      ge_input: submission.input?.ge_input || '',
      created_at: submission.created_at,
      updated_at: submission.updated_at,
    };

    res.json({
      success: true,
      data: response,
    } as ApiResponse<StatusResponse>);
  } catch (error: any) {
    logger.error('Error in /api/submissions/:id', { error: error.message });
    res.status(500).json({
      success: false,
      error: error.message,
    } as ApiResponse);
  }
});

/**
 * GET /api/submissions
 * Get submissions history with optional limit
 */
app.get('/api/submissions', async (req: Request, res: Response) => {
  try {
    const limit = parseInt(req.query.limit as string) || 30;

    const submissions = await supabaseService.getSubmissionsHistory(limit);

    res.json({
      success: true,
      data: submissions,
    } as ApiResponse<SubmissionRecord[]>);
  } catch (error: any) {
    logger.error('Error in /api/submissions', { error: error.message });
    res.status(500).json({
      success: false,
      error: error.message,
    } as ApiResponse);
  }
});

/**
 * DELETE /api/submissions/:id
 * Delete a submission
 */
app.delete('/api/submissions/:id', async (req: Request, res: Response) => {
  try {
    const submissionId = req.params.id;

    await supabaseService.deleteSubmission(submissionId);

    res.json({
      success: true,
      data: { message: 'Submission deleted successfully' },
    } as ApiResponse);
  } catch (error: any) {
    logger.error('Error in DELETE /api/submissions/:id', { error: error.message });
    res.status(500).json({
      success: false,
      error: error.message,
    } as ApiResponse);
  }
});

/**
 * POST /api/submissions/:id/retry
 * Retry a failed submission with only the error GE IDs and error usernames
 */
app.post('/api/submissions/:id/retry', async (req: Request, res: Response) => {
  try {
    const submissionId = req.params.id;
    const { errorGeIds, errorUsernames } = req.body;

    let submission;
    if (errorGeIds && errorUsernames && errorGeIds.length > 0 && errorUsernames.length > 0) {
      // Create new submission with only error GE IDs and usernames
      submission = await supabaseService.createRetrySubmission(errorUsernames, errorGeIds.join('\n'));
    } else {
      // Legacy behavior: reset status to pending
      submission = await supabaseService.retrySubmission(submissionId);
    }

    res.json({
      success: true,
      data: {
        submission_id: submission.submission_id,
        status: submission.status,
        message: 'Submission queued for retry',
      },
    } as ApiResponse);
  } catch (error: any) {
    logger.error('Error in POST /api/submissions/:id/retry', { error: error.message });
    res.status(500).json({
      success: false,
      error: error.message,
    } as ApiResponse);
  }
});

/**
 * GET /api/queue
 * Get TMS permission queue items (only the CURRENT processing submission)
 * Pending submissions are counted but not shown in detail
 */
app.get('/api/queue', async (req: Request, res: Response) => {
  try {
    const submissions = await supabaseService.getSubmissionsHistory(100);

    // Filter to only pending and processing
    const activeSubmissions = submissions.filter(
      s => s.status === 'pending' || s.status === 'processing'
    );

    // Find the currently processing submission (should be at most 1)
    const processingSubmission = activeSubmissions.find(s => s.status === 'processing');
    
    // Count pending submissions (waiting in queue)
    const pendingCount = activeSubmissions.filter(s => s.status === 'pending').length;

    // Only show GE IDs from the processing submission
    const queueItems: any[] = [];

    if (processingSubmission) {
      const input = processingSubmission.input;
      if (input) {
        const usernames = input.username_list || [];
        const usernamesStr = usernames.join('\n');
        const geInput = input.ge_input || '';
        const results = processingSubmission.results || [];

        // Parse GE input lines
        const lines = geInput.split('\n').filter((l: string) => l.trim());

        for (let idx = 0; idx < lines.length; idx++) {
          const line = lines[idx].trim();
          const parts = line.split(/\s+/);
          const geId = parts[0] || line;
          const lang = parts[1] || '';

          // Check if this GE ID has results
          const geResults = results.filter(
            (r: any) => r.ge_id === geId && r.lang?.toLowerCase() === lang?.toLowerCase()
          );

          // Determine status based on results
          // Only 2 final statuses: done (green), error (red)
          // If no results yet: waiting (gray)
          let mappedStatus = 'waiting';
          if (geResults.length > 0) {
            const hasError = geResults.some((r: any) => r.status === 'error');
            mappedStatus = hasError ? 'error' : 'done';
          }

          queueItems.push({
            key: `${processingSubmission.submission_id}:${idx}`,
            id: geId,
            lang: lang,
            status: mappedStatus,
            usernames: usernamesStr,
            submission_id: processingSubmission.submission_id,
          });
        }
      }
    }

    res.json({
      success: true,
      queue: queueItems,
      pendingCount: pendingCount, // Number of submissions waiting in queue
    });
  } catch (error: any) {
    logger.error('Error in GET /api/queue', { error: error.message });
    res.status(500).json({
      success: false,
      error: error.message,
    });
  }
});

/**
 * GET /api/usernames
 * Get list of usernames for autocomplete
 */
app.get('/api/usernames', async (req: Request, res: Response) => {
  try {
    const usernames = await supabaseService.getAllUsernames();

    res.json({
      success: true,
      data: usernames,
    } as ApiResponse<string[]>);
  } catch (error: any) {
    logger.error('Error in /api/usernames', { error: error.message });
    res.status(500).json({
      success: false,
      error: error.message,
    } as ApiResponse);
  }
});

/**
 * POST /api/user/search
 * Search TMS users with optional project filter
 */
app.post('/api/user/search', async (req: Request, res: Response) => {
  try {
    const { query, excludeProjectId } = req.body;

    if (!query) {
      return res.status(400).json({
        success: false,
        error: 'Missing query parameter',
      } as ApiResponse);
    }

    const results = await tmsApiService.searchUser(query, excludeProjectId);

    res.json({
      success: true,
      data: results,
    } as ApiResponse);
  } catch (error: any) {
    logger.error('Error in /api/user/search', { error: error.message });
    res.status(500).json({
      success: false,
      error: error.message,
    } as ApiResponse);
  }
});

/**
 * POST /api/project-info
 * Get TMS project information from GE ID & Lang
 */
app.post('/api/project-info', async (req: Request, res: Response) => {
  try {
    const { geId, lang } = req.body;

    if (!geId || !lang) {
      return res.status(400).json({
        success: false,
        error: 'Missing geId or lang parameter',
      } as ApiResponse);
    }

    // Get TMS ID from MongoDB
    const tmsIdStr = await mongodbService.getTmsId(geId, lang);
    if (!tmsIdStr) {
      return res.status(404).json({
        success: false,
        error: `TMS ID not found for GE ${geId} ${lang}`,
      } as ApiResponse);
    }

    const tmsId = parseInt(tmsIdStr, 10);
    if (isNaN(tmsId)) {
      return res.status(400).json({
        success: false,
        error: `Invalid TMS ID: ${tmsIdStr}`,
      } as ApiResponse);
    }

    // Get project details from TMS API (with correct locale)
    const projectDetails = await tmsApiService.getProjectDetails(tmsId, lang);

    res.json({
      success: true,
      data: {
        ...projectDetails,
        lang: lang.toUpperCase(), // Include lang for subsequent operations
      },
    } as ApiResponse);
  } catch (error: any) {
    logger.error('Error in /api/project-info', { error: error.message });
    res.status(500).json({
      success: false,
      error: error.message,
    } as ApiResponse);
  }
});

/**
 * DELETE /api/project-member
 * Delete a member from TMS project
 */
app.delete('/api/project-member', async (req: Request, res: Response) => {
  try {
    const { projectId, email, lang } = req.body;

    if (!projectId || !email) {
      return res.status(400).json({
        success: false,
        error: 'Missing projectId or email parameter',
      } as ApiResponse);
    }

    await tmsApiService.deleteProjectMember(projectId, email, lang);

    res.json({
      success: true,
      data: { message: `Deleted ${email} from project ${projectId}` },
    } as ApiResponse);
  } catch (error: any) {
    logger.error('Error in /api/project-member DELETE', { error: error.message });
    res.status(500).json({
      success: false,
      error: error.message,
    } as ApiResponse);
  }
});

/**
 * POST /api/user/resolve-emails
 * Resolve multiple emails to TMS usernames
 */
app.post('/api/user/resolve-emails', async (req: Request, res: Response) => {
  try {
    const { emails } = req.body;

    if (!emails || !Array.isArray(emails)) {
      return res.status(400).json({
        success: false,
        error: 'Missing or invalid emails array',
      } as ApiResponse);
    }

    const emailToUsername = await tmsApiService.resolveEmailsToUsernames(emails);

    res.json({
      success: true,
      data: emailToUsername,
    } as ApiResponse);
  } catch (error: any) {
    logger.error('Error in /api/user/resolve-emails', { error: error.message });
    res.status(500).json({
      success: false,
      error: error.message,
    } as ApiResponse);
  }
});

/**
 * POST /api/check/submit
 * Submit check upload request
 */
app.post('/api/check/submit', checkController.submitCheck);

/**
 * GET /api/check/history
 * Get check upload history
 */
app.get('/api/check/history', checkController.getCheckHistory);

/**
 * DELETE /api/check/:id
 * Delete a check record
 */
app.delete('/api/check/:id', checkController.deleteCheck);

/**
 * GET /health
 * Health check endpoint
 */
app.get('/health', (req: Request, res: Response) => {
  res.json({
    success: true,
    data: {
      status: 'healthy',
      timestamp: new Date().toISOString(),
      service: 'TMS Permission Backend',
    },
  });
});

/**
 * Reset stuck submissions
 * On server startup, reset any submissions stuck in 'processing' status to 'pending'
 * This handles the case where server crashed/restarted during processing
 */
async function resetStuckSubmissions(): Promise<void> {
  try {
    const stuckSubmissions = await supabaseService.getProcessingSubmissions();

    if (stuckSubmissions.length === 0) {
      logger.debug('No stuck submissions found');
      return;
    }

    logger.warn(`Found ${stuckSubmissions.length} stuck submission(s), resetting to pending...`);

    for (const sub of stuckSubmissions) {
      try {
        await supabaseService.updateSubmissionStatus(sub.submission_id, 'pending');
        logger.debug(`Reset stuck submission: ${sub.submission_id}`);
      } catch (error: any) {
        logger.error(`Failed to reset submission ${sub.submission_id}:`, { error: error.message });
      }
    }

    logger.debug('Finished resetting stuck submissions');
  } catch (error: any) {
    logger.error('Error checking for stuck submissions', { error: error.message });
  }
}

/**
 * Start server
 */
async function startServer() {
  try {
    // Connect to MongoDB
    await mongodbService.connect();

    // Reset any stuck submissions (processing status from previous server instance)
    await resetStuckSubmissions();

    // Start worker
    startWorker();

    // Start Express server
    app.listen(PORT, () => {
      logger.debug('🚀 TMS Backend server running on port ' + PORT);
      logger.debug('Health check: http://localhost:' + PORT + '/health');
    });

    // Graceful shutdown
    process.on('SIGINT', shutdown);
    process.on('SIGTERM', shutdown);
  } catch (error: any) {
    logger.error('Failed to start server', { error: error.message });
    process.exit(1);
  }
}

/**
 * Graceful shutdown
 */
async function shutdown() {
  logger.debug('Shutting down gracefully...');

  stopWorker();
  await mongodbService.close();

  process.exit(0);
}

// Start the server
startServer();
