import { spawn } from 'child_process';
import { promises as fs } from 'fs';
import { join } from 'path';
import { homedir } from 'os';
import log from 'electron-log';
import * as yaml from 'js-yaml';
import { BrowserWindow } from 'electron';
import { getEnvWithPath } from '../utils/shell-utils';
import { createOutputSender } from '../utils/ipc-output-helper';
import { getBackups, getCanvasPostgresContainers, restoreBackupWithSender } from './backup-manager';

export interface BuildRunStatus {
  instDomain: string | null;
  canvasUrl: string | null;
  siteAccessible: boolean;
  configured: boolean;
  folderName: string | null;
}

const INST_CONFIG_PATH = join(homedir(), '.inst-cli', 'inst.yml');

/**
 * Check the status of build and run by verifying site accessibility
 */
export async function checkBuildRunStatus(multiDeploy = true): Promise<BuildRunStatus> {
  try {
    let instDomain: string | null = null;
    let folderName: string | null = null;

    try {
      const instConfigContent = await fs.readFile(INST_CONFIG_PATH, 'utf-8');
      const instConfig: any = yaml.load(instConfigContent);
      instDomain = instConfig?.inst?.inst_domain || null;

      const sourcePath: string | null = instConfig?.inst?.['canvas-lms']?.source || null;
      if (sourcePath) {
        folderName = require('path').basename(sourcePath);
      }
    } catch (error: any) {
      if (error.code !== 'ENOENT') {
        log.error('Error reading inst.yml:', error);
      }
    }

    let canvasUrl: string | null = null;
    if (instDomain) {
      canvasUrl = multiDeploy && folderName
        ? `http://canvas-web-${folderName}.${instDomain}`
        : `http://canvas-web.${instDomain}`;
    }

    let siteAccessible = false;
    if (canvasUrl) {
      try {
        const response = await fetch(canvasUrl, {
          method: 'HEAD',
          signal: AbortSignal.timeout(5000)
        });
        siteAccessible = response.ok;
      } catch {
        siteAccessible = false;
      }
    }

    return {
      instDomain,
      canvasUrl,
      siteAccessible,
      configured: instDomain !== null && siteAccessible,
      folderName
    };
  } catch (error) {
    log.error('Error checking build and run status:', error);
    throw error;
  }
}

/**
 * Shared implementation – runs an inst canvas command and streams output.
 * After a successful run the default backup (if any) is restored automatically.
 */
async function runSetup(
  command: string,
  title: string,
  window?: BrowserWindow
): Promise<{ success: boolean; message: string }> {
  const sendOutput = createOutputSender(window ?? null, 'build-run:output');

  try {
    sendOutput('═══════════════════════════════════════════════════');
    sendOutput(title);
    sendOutput('═══════════════════════════════════════════════════');
    sendOutput(`Running: ${command}\n`);

    return new Promise((resolve) => {
      const env = {
        ...getEnvWithPath(),
        TERM: 'xterm-256color',
        COLORTERM: 'truecolor',
        FORCE_COLOR: '3',
        CLICOLOR: '1',
        CLICOLOR_FORCE: '1',
      };

      const instProcess = spawn('/bin/zsh', ['-c', command], { env });

      let stdout = '';
      let stderr = '';

      instProcess.stdout.on('data', (data) => {
        const output = data.toString();
        stdout += output;
        sendOutput(output);
      });

      instProcess.stderr.on('data', (data) => {
        const output = data.toString();
        stderr += output;
        sendOutput(output);
      });

      instProcess.on('error', (error) => {
        sendOutput(`\n✗ Error: ${error.message}`);
        resolve({
          success: false,
          message: `Failed to build and run: ${error.message}`
        });
      });

      instProcess.on('close', async (code) => {
        if (code === 0) {
          sendOutput('\n✓ Build and run completed successfully!');
          sendOutput('\n═══════════════════════════════════════════════════');
          sendOutput('Build and Run Complete');
          sendOutput('═══════════════════════════════════════════════════');

          // Restore the default backup, if one is configured
          try {
            const backups = await getBackups();
            const defaultBackup = backups.find((b) => b.isDefault);
            if (defaultBackup) {
              const containers = await getCanvasPostgresContainers();
              const container = containers[0];
              if (container) {
                sendOutput('\n\n═══════════════════════════════════════════════════');
                sendOutput('Restoring Default Backup');
                sendOutput(`Backup: ${defaultBackup.fileName}`);
                sendOutput(`Target: ${container}`);
                sendOutput('═══════════════════════════════════════════════════\n');
                const restoreResult = await restoreBackupWithSender(
                  defaultBackup.filePath,
                  container,
                  sendOutput
                );
                if (!restoreResult.success) {
                  sendOutput(`\n⚠ Default backup restore failed: ${restoreResult.message}\n`);
                }
              } else {
                sendOutput('\n⚠ Default backup found but no canvas-postgres container is running — skipping restore.\n');
              }
            }
          } catch (err: any) {
            log.error('Error restoring default backup after build:', err);
            sendOutput(`\n⚠ Could not restore default backup: ${err.message}\n`);
          }

          resolve({
            success: true,
            message: 'Build and run completed successfully!'
          });
        } else {
          sendOutput(`\n✗ inst canvas setup failed with exit code ${code}`);
          sendOutput('\n═══════════════════════════════════════════════════');

          resolve({
            success: false,
            message: `Failed to build and run (exit code ${code})`
          });
        }
      });
    });
  } catch (error: any) {
    log.error('Error building and running:', error);
    sendOutput(`\n✗ Unexpected error: ${error.message}`);
    return {
      success: false,
      message: error.message || 'Failed to build and run'
    };
  }
}

/**
 * Build and run Canvas by executing inst canvas setup
 */
export async function buildAndRun(window?: BrowserWindow, multiDeploy = true): Promise<{ success: boolean; message: string }> {
  const setup = 'printf "%s" "2" "2" | inst canvas setup';
  const command = multiDeploy
    ? `source ~/.inst_functions && inst canvas down && inst canvas multideploy && ${setup}`
    : `source ~/.inst_functions && inst canvas down && inst canvas singledeploy && ${setup}`;
  return runSetup(command, 'Building and Running Canvas', window);
}

/**
 * Clean, build and run Canvas by executing inst canvas clean && inst canvas setup
 */
export async function cleanBuildAndRun(window?: BrowserWindow, multiDeploy = true): Promise<{ success: boolean; message: string }> {
  const setup = 'inst canvas clean && printf "%s" "2" "2" | inst canvas setup';
  const command = multiDeploy
    ? `source ~/.inst_functions && inst canvas down && inst canvas multideploy && ${setup}`
    : `source ~/.inst_functions && inst canvas down && inst canvas singledeploy && ${setup}`;
  return runSetup(command, 'Clean, Build and Run Canvas', window);
}
