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

export interface InstCliProxyStatus {
  instDomain: string | null;
  resolverFileExists: boolean;
  resolverFilePath: string | null;
  dockerComposeExists: boolean;
  dockerComposePath: string;
  traefikRunning: boolean;
  traefikContainerCount: number;
  configured: boolean;
}

const INST_CONFIG_PATH = join(homedir(), '.inst-cli', 'inst.yml');
const DOCKER_COMPOSE_OVERRIDE_CONTENT = `services:
  dozzle:
    container_name: dozzle
    image: amir20/dozzle:v6.6.3
    env_file:
      - .env
    environment:
      DOZZLE_LEVEL: debug
      DOZZLE_NO_ANALYTICS: 1
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
    labels:
      - traefik.enable=true
      - traefik.http.routers.dozzle-traefik-router.rule=Host(\`logs.$INST_DOMAIN\`)
      - traefik.http.routers.dozzle-traefik-router.service=dozzle-traefik
      - traefik.http.services.dozzle-traefik.loadbalancer.server.port=8080
`;

/**
 * Check the status of inst-cli proxy configuration
 */
export async function checkInstCliProxyStatus(): Promise<InstCliProxyStatus> {
  try {
    // Read inst.yml to get inst_domain
    let instDomain: 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;
    } catch (error: any) {
      if (error.code !== 'ENOENT') {
        log.error('Error reading inst.yml:', error);
      }
    }

    // Check if resolver file exists
    let resolverFileExists = false;
    let resolverFilePath: string | null = null;
    if (instDomain) {
      resolverFilePath = `/etc/resolver/${instDomain}`;
      try {
        await fs.access(resolverFilePath);
        resolverFileExists = true;
      } catch {
        resolverFileExists = false;
      }
    }

    // Check if docker-compose.override.yml exists
    const dockerComposePath = join(homedir(), '.inst-cli', 'traefik', 'docker-compose.override.yml');
    let dockerComposeExists = false;
    try {
      await fs.access(dockerComposePath);
      dockerComposeExists = true;
    } catch {
      dockerComposeExists = false;
    }

    // Check if traefik docker compose is running with 3 containers
    let traefikRunning = false;
    let traefikContainerCount = 0;
    try {
      const { stdout } = await execAsync('docker compose ls');
      const lines = stdout.trim().split('\n');

      // Skip header line and find traefik entry
      for (let i = 1; i < lines.length; i++) {
        const line = lines[i].trim();
        if (line.startsWith('traefik')) {
          // Parse the line: NAME STATUS CONFIG FILES
          // Example: traefik             running(3)          /path/to/docker-compose.yml
          const match = line.match(/traefik\s+running\((\d+)\)/);
          if (match) {
            traefikRunning = true;
            traefikContainerCount = parseInt(match[1], 10);
          }
          break;
        }
      }
    } catch (error: any) {
      log.warn('Error checking docker compose status:', error.message);
      // Don't throw, just leave as false
    }

    const configured = instDomain !== null &&
                      resolverFileExists &&
                      dockerComposeExists &&
                      traefikRunning &&
                      traefikContainerCount === 3;

    return {
      instDomain,
      resolverFileExists,
      resolverFilePath,
      dockerComposeExists,
      dockerComposePath,
      traefikRunning,
      traefikContainerCount,
      configured
    };
  } catch (error) {
    log.error('Error checking inst-cli proxy status:', error);
    throw error;
  }
}

/**
 * Create docker-compose.override.yml file
 */
export async function createDockerComposeOverride(): Promise<{ success: boolean; message: string }> {
  try {
    const dockerComposePath = join(homedir(), '.inst-cli', 'traefik', 'docker-compose.override.yml');

    // Ensure the directory exists
    const dir = join(homedir(), '.inst-cli', 'traefik');
    await fs.mkdir(dir, { recursive: true });

    // Write the file
    await fs.writeFile(dockerComposePath, DOCKER_COMPOSE_OVERRIDE_CONTENT, 'utf-8');
    log.info('docker-compose.override.yml created successfully');

    return {
      success: true,
      message: 'docker-compose.override.yml created successfully'
    };
  } catch (error: any) {
    log.error('Error creating docker-compose.override.yml:', error);
    return {
      success: false,
      message: error.message || 'Failed to create docker-compose.override.yml'
    };
  }
}

/**
 * Start the proxy by creating the resolver file and running inst proxy start
 */
export async function startProxy(window?: BrowserWindow): Promise<{ success: boolean; message: string }> {
  const sendOutput = createOutputSender(window ?? null, 'inst-cli-proxy:start-output');

  try {
    sendOutput('═══════════════════════════════════════════════════');
    sendOutput('Starting inst-cli Proxy');
    sendOutput('═══════════════════════════════════════════════════');

    // Read inst.yml to get inst_domain
    let instDomain: 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;
    } catch (error: any) {
      sendOutput('✗ Failed to read inst_domain from inst.yml');
      return {
        success: false,
        message: 'Failed to read inst_domain from inst.yml. Please configure it first.'
      };
    }

    if (!instDomain) {
      sendOutput('✗ inst_domain not found in inst.yml');
      return {
        success: false,
        message: 'inst_domain not found in inst.yml. Please configure it first.'
      };
    }

    sendOutput(`Found inst_domain: ${instDomain}`);

    // Create resolver file content
    const resolverPath = `/etc/resolver/${instDomain}`;

    sendOutput('\nCreating resolver file (admin password required)...');

    // Use osascript to create the resolver file with admin privileges
    // Use two echo commands to avoid quote escaping issues
    const createResolverScript = `
do shell script "mkdir -p /etc/resolver && echo nameserver 127.0.0.1 > ${resolverPath} && echo port 19321 >> ${resolverPath}" with administrator privileges
    `.trim();

    try {
      await execAsync(`osascript -e '${createResolverScript}'`);
      sendOutput(`✓ Resolver file created at ${resolverPath}`);
      log.info('Resolver file created successfully:', resolverPath);
    } catch (error: any) {
      log.error('Error creating resolver file:', error);
      sendOutput('✗ Failed to create resolver file');
      return {
        success: false,
        message: 'Failed to create resolver file. ' + (error.message || '')
      };
    }

    // Run inst proxy start with streaming output
    sendOutput('\nStarting inst proxy...');
    sendOutput('Running: inst proxy start\n');

    return new Promise((resolve) => {
      // Run through shell with manual sourcing to access shell functions
      const instProcess = spawn('/bin/zsh', ['-c', 'source ~/.inst_functions && inst proxy start'], {
        env: getEnvWithPath()
      });

      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 start inst proxy: ${error.message}`
        });
      });

      instProcess.on('close', (code) => {
        if (code === 0) {
          sendOutput('\n✓ Proxy started successfully!');
          sendOutput('\n═══════════════════════════════════════════════════');
          sendOutput('Proxy Start Complete');
          sendOutput('═══════════════════════════════════════════════════');

          resolve({
            success: true,
            message: 'Proxy started successfully!'
          });
        } else {
          sendOutput(`\n✗ inst proxy start failed with exit code ${code}`);
          sendOutput('\n═══════════════════════════════════════════════════');

          resolve({
            success: false,
            message: `Failed to start proxy (exit code ${code})`
          });
        }
      });
    });
  } catch (error: any) {
    log.error('Error starting proxy:', error);
    sendOutput(`\n✗ Unexpected error: ${error.message}`);
    return {
      success: false,
      message: error.message || 'Failed to start proxy'
    };
  }
}

/**
 * Stop the proxy by running inst proxy stop command
 */
export async function stopProxy(window?: BrowserWindow): Promise<{ success: boolean; message: string }> {
  const sendOutput = createOutputSender(window ?? null, 'inst-cli-proxy:stop-output');

  try {
    sendOutput('═══════════════════════════════════════════════════');
    sendOutput('Stopping inst-cli Proxy');
    sendOutput('═══════════════════════════════════════════════════');
    sendOutput('Running: inst proxy stop\n');

    return new Promise((resolve) => {
      // Run through shell with manual sourcing to access shell functions
      const instProcess = spawn('/bin/zsh', ['-c', 'source ~/.inst_functions && inst proxy stop'], {
        env: getEnvWithPath()
      });

      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 stop inst proxy: ${error.message}`
        });
      });

      instProcess.on('close', (code) => {
        if (code === 0) {
          sendOutput('\n✓ Proxy stopped successfully!');
          sendOutput('\n═══════════════════════════════════════════════════');
          sendOutput('Proxy Stop Complete');
          sendOutput('═══════════════════════════════════════════════════');

          resolve({
            success: true,
            message: 'Proxy stopped successfully!'
          });
        } else {
          sendOutput(`\n✗ inst proxy stop failed with exit code ${code}`);
          sendOutput('\n═══════════════════════════════════════════════════');

          resolve({
            success: false,
            message: `Failed to stop proxy (exit code ${code})`
          });
        }
      });
    });
  } catch (error: any) {
    log.error('Error stopping proxy:', error);
    sendOutput(`\n✗ Unexpected error: ${error.message}`);
    return {
      success: false,
      message: error.message || 'Failed to stop proxy'
    };
  }
}
