import { promises as fs } from 'fs';
import { join } from 'path';
import { homedir } from 'os';
import log from 'electron-log';
import { execAsync } from '../utils/shell-utils';
import { checkGemrcStatus } from './gemrc-manager';
import { settingsStore } from '../store';
import * as yaml from 'js-yaml';

export interface FrpcConfigStatus {
  serviceFileExists: boolean;
  configFileExists: boolean;
  isRunning: boolean;
  configured: boolean;
  canvasUrl: string | null;
  folderName: string | null;
  frpcUsername: string | null;
}

const LAUNCHAGENT_PATH = join(homedir(), 'Library', 'LaunchAgents', 'com.fatedier.frpc.plist');
const FRP_DIR = join(homedir(), 'frp');
const CONFIG_PATH = join(FRP_DIR, 'frpc.toml');
const AUTH_TOKEN_FILE = join(FRP_DIR, '.frpc-auth-token');

/**
 * Resolve the FRPC auth token.
 * Priority: FRPC_AUTH_TOKEN env var → ~/frp/.frpc-auth-token file
 */
async function resolveFrpcAuthToken(): Promise<string> {
  if (process.env.FRPC_AUTH_TOKEN) {
    log.info('FRPC auth token loaded from environment variable');
    return process.env.FRPC_AUTH_TOKEN;
  }
  try {
    const token = (await fs.readFile(AUTH_TOKEN_FILE, 'utf-8')).trim();
    if (token) {
      log.info('FRPC auth token loaded from', AUTH_TOKEN_FILE);
      return token;
    }
  } catch {
    // file doesn't exist
  }
  try {
    const bundledTokenFile = join(process.resourcesPath, '.frpc-auth-token');
    const token = (await fs.readFile(bundledTokenFile, 'utf-8')).trim();
    if (token) {
      log.info('FRPC auth token loaded from bundled resources');
      return token;
    }
  } catch {
    // not bundled or not in packaged app
  }
  log.warn(`FRPC auth token not found. Set FRPC_AUTH_TOKEN env var or create ${AUTH_TOKEN_FILE}`);
  return '';
}

const PLIST_TEMPLATE = `<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/applecomputerdtds/propertylist-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.fatedier.frpc</string>
    <key>ProgramArguments</key>
    <array>
        <string>/opt/homebrew/bin/frpc</string>
        <string>-c</string>
        <string>{{FRP_CONFIG_PATH}}</string>
    </array>
    <key>EnvironmentVariables</key>
    <dict>
        <key>FRPC_AUTH_TOKEN</key>
        <string>{{FRPC_AUTH_TOKEN}}</string>
    </dict>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <true/>
    <key>WorkingDirectory</key>
    <string>{{FRP_DIR}}</string>
    <key>StandardOutPath</key>
    <string>{{FRP_DIR}}/frpc.log</string>
    <key>StandardErrorPath</key>
    <string>{{FRP_DIR}}/frpc_error.log</string>
</dict>
</plist>
`;

const FRPC_CONFIG_TEMPLATE = `# Global settings for connecting to the FRPS server
serverAddr = "172.12.61.134"
serverPort = 7123
auth.token = "{{FRPC_AUTH_TOKEN}}"

[[proxies]]
name = "$USERNAME"
type = "http"
localPort = 80
# FRP sees the Host header Nginx sent, which is the internal one
customDomains = ["canvas-web.inseng.test"]
`;

/**
 * Check FRPC service status
 */
export async function checkFrpcConfigStatus(): Promise<FrpcConfigStatus> {
  try {
    // Check if service file exists
    let serviceFileExists = false;
    try {
      await fs.access(LAUNCHAGENT_PATH);
      serviceFileExists = true;
    } catch {
      serviceFileExists = false;
    }

    // Check if config file exists
    let configFileExists = false;
    try {
      await fs.access(CONFIG_PATH);
      configFileExists = true;
    } catch {
      configFileExists = false;
    }

    // Check if service is running
    let isRunning = false;
    try {
      const { stdout } = await execAsync('launchctl list | grep com.fatedier.frpc');
      isRunning = stdout.trim().length > 0;
    } catch {
      isRunning = false;
    }

    const configured = serviceFileExists && configFileExists && isRunning;

    // Resolve canvas URL from inst.yml and frpc.toml
    let canvasUrl: string | null = null;
    let folderName: string | null = null;

    // Read username from frpc.toml [[proxies]] name field
    let frpcUsername: string | null = null;
    try {
      const frpcConfigContent = await fs.readFile(CONFIG_PATH, 'utf-8');
      const lines = frpcConfigContent.split('\n');
      let inProxies = false;
      for (const line of lines) {
        if (line.trim() === '[[proxies]]') {
          inProxies = true;
          continue;
        }
        if (inProxies) {
          const nameMatch = line.match(/^name\s*=\s*"([^"]+)"/);
          if (nameMatch) {
            frpcUsername = nameMatch[1];
            break;
          }
        }
      }
    } catch (error: any) {
      if (error.code !== 'ENOENT') {
        log.error('Error reading frpc.toml for username:', error);
      }
    }

    try {
      const instConfigPath = join(homedir(), '.inst-cli', 'inst.yml');
      const instConfigContent = await fs.readFile(instConfigPath, 'utf-8');
      const instConfig: any = yaml.load(instConfigContent);
      const multiDeploy = settingsStore.get('multiDeploy');

      const sourcePath: string | null = instConfig?.inst?.['canvas-lms']?.source || null;
      if (sourcePath) {
        folderName = require('path').basename(sourcePath);
      }

      if (multiDeploy && folderName && frpcUsername) {
        canvasUrl = `https://canvas-web-${folderName}-${frpcUsername}.instructure.cloud`;
      } else if (frpcUsername) {
        canvasUrl = `https://canvas-web-${frpcUsername}.instructure.cloud`;
      }
    } catch (error: any) {
      if (error.code !== 'ENOENT') {
        log.error('Error reading inst.yml for FRPC config status:', error);
      }
    }

    return {
      serviceFileExists,
      configFileExists,
      isRunning,
      configured,
      canvasUrl,
      folderName,
      frpcUsername
    };
  } catch (error) {
    log.error('Error checking FRPC config status:', error);
    return {
      serviceFileExists: false,
      configFileExists: false,
      isRunning: false,
      configured: false,
      canvasUrl: null,
      folderName: null,
      frpcUsername: null
    };
  }
}

/**
 * Start FRPC service
 */
export async function startFrpcService(): Promise<{ success: boolean; message: string }> {
  try {
    // Ensure FRP directory exists
    await fs.mkdir(FRP_DIR, { recursive: true });
    log.info('Created FRP directory:', FRP_DIR);

    // Read auth token from env var or token file
    const frpcAuthToken = await resolveFrpcAuthToken();

    // Create plist file (inject token so the service has it in its environment)
    const plistContent = PLIST_TEMPLATE
      .replace(/\{\{FRP_CONFIG_PATH\}\}/g, CONFIG_PATH)
      .replace(/\{\{FRP_DIR\}\}/g, FRP_DIR)
      .replace(/\{\{FRPC_AUTH_TOKEN\}\}/g, frpcAuthToken);

    const launchAgentsDir = join(homedir(), 'Library', 'LaunchAgents');
    await fs.mkdir(launchAgentsDir, { recursive: true });
    await fs.writeFile(LAUNCHAGENT_PATH, plistContent, 'utf-8');
    log.info('Created LaunchAgent plist file');

    // Resolve JFrog username from .gemrc, sanitize to lowercase [a-z] only
    let frpcUsername = 'user';
    try {
      const gemrc = await checkGemrcStatus();
      if (gemrc.username) {
        frpcUsername = gemrc.username.toLowerCase().replace(/[^a-z]/g, '');
      }
    } catch (err) {
      log.warn('Could not read JFrog username from .gemrc, using default:', err);
    }
    log.info('Using sanitized FRPC username:', frpcUsername);

    // Create config file with substituted username and auth token
    const frpcConfigContent = FRPC_CONFIG_TEMPLATE
      .replace('$USERNAME', frpcUsername)
      .replace('{{FRPC_AUTH_TOKEN}}', frpcAuthToken);
    await fs.writeFile(CONFIG_PATH, frpcConfigContent, 'utf-8');
    log.info('Created FRPC config file');

    // Unload if already loaded (to handle updates)
    try {
      await execAsync(`launchctl unload "${LAUNCHAGENT_PATH}"`);
    } catch {
      // Ignore error if not loaded
    }

    // Load the service
    await execAsync(`launchctl load "${LAUNCHAGENT_PATH}"`);
    log.info('Loaded FRPC service');

    // Start the service
    await execAsync('launchctl start com.fatedier.frpc');
    log.info('Started FRPC service');

    // Wait a bit and check if running
    await new Promise(resolve => setTimeout(resolve, 1000));

    const { stdout } = await execAsync('launchctl list | grep com.fatedier.frpc');
    if (stdout.trim().length > 0) {
      // Update FRP custom domains now that the service is running
      try {
        await execAsync('source ~/.inst_functions && update-frpc', { shell: '/bin/zsh' });
        log.info('update-frpc ran successfully');
      } catch (err) {
        log.warn('update-frpc failed (non-fatal):', err);
      }

      return {
        success: true,
        message: 'FRPC service started successfully'
      };
    } else {
      return {
        success: false,
        message: 'FRPC service failed to start'
      };
    }
  } catch (error: any) {
    log.error('Error starting FRPC service:', error);
    return {
      success: false,
      message: error.message || 'Failed to start FRPC service'
    };
  }
}

/**
 * Stop FRPC service
 */
export async function stopFrpcService(): Promise<{ success: boolean; message: string }> {
  try {
    // Stop the service
    try {
      await execAsync('launchctl stop com.fatedier.frpc');
      log.info('Stopped FRPC service');
    } catch (error: any) {
      log.warn('Error stopping service (may not be running):', error.message);
    }

    // Unload the service
    try {
      await execAsync(`launchctl unload "${LAUNCHAGENT_PATH}"`);
      log.info('Unloaded FRPC service');
    } catch (error: any) {
      log.warn('Error unloading service:', error.message);
    }

    // Verify it's stopped
    await new Promise(resolve => setTimeout(resolve, 500));

    try {
      const { stdout } = await execAsync('launchctl list | grep com.fatedier.frpc');
      if (stdout.trim().length > 0) {
        return {
          success: false,
          message: 'FRPC service is still running'
        };
      }
    } catch {
      // grep returns non-zero if no match, which is what we want
    }

    return {
      success: true,
      message: 'FRPC service stopped successfully'
    };
  } catch (error: any) {
    log.error('Error stopping FRPC service:', error);
    return {
      success: false,
      message: error.message || 'Failed to stop FRPC service'
    };
  }
}
