const fs = require('fs');
const path = require('path');
const yaml = require('js-yaml');
const { buildTablePlusUrl, getDbDriver } = require('../utils');

class DdevProvider {
  constructor({ runCommand, runInteractiveCommand }) {
    this.runCommand = runCommand;
    this.runInteractiveCommand = runInteractiveCommand || runCommand;
  }

  async start(project, options = {}) {
    await this.runStartStopCommand('ddev', ['start'], project, options);
    return this.status(project);
  }

  async stop(project, options = {}) {
    await this.runStartStopCommand('ddev', ['stop'], project, options);
    return {
      projectId: project.id,
      running: false,
      exposed: false,
      health: 'stopped',
      lastSeenAt: new Date().toISOString()
    };
  }

  async runStartStopCommand(command, args, project, options) {
    const runner = options.interactive ? this.runInteractiveCommand : this.runCommand;
    await runner(command, args, { cwd: project.path });
  }

  async status(project) {
    try {
      const { stdout } = await this.runCommand('ddev', ['describe', '--json-output'], {
        cwd: project.path,
        timeout: 15000,
        maxBuffer: 10 * 1024 * 1024
      });
      const parsed = JSON.parse(stdout);
      return this.runtimeFromDescribe(project, parsed.raw || parsed);
    } catch (error) {
      if (error.code === 'ENOENT') {
        throw Object.assign(new Error('DDEV is not installed or is not on PATH.'), { code: 'DDEV_NOT_FOUND' });
      }

      return {
        projectId: project.id,
        running: false,
        exposed: false,
        health: 'unknown',
        lastSeenAt: new Date().toISOString()
      };
    }
  }

  async databaseUrl(project) {
    const { stdout } = await this.runCommand('ddev', ['describe', '--json-output'], {
      cwd: project.path,
      timeout: 15000,
      maxBuffer: 10 * 1024 * 1024
    });
    const parsed = JSON.parse(stdout);
    const raw = parsed.raw || parsed;

    if (!raw.dbinfo) {
      const error = new Error(`No DDEV database information is available for ${project.name}.`);
      error.code = 'DATABASE_NOT_AVAILABLE';
      throw error;
    }

    const dbConfig = {
      host: '127.0.0.1',
      port: raw.dbinfo.published_port || 3306,
      user: raw.dbinfo.username || 'db',
      password: raw.dbinfo.password || 'db',
      database: raw.dbinfo.dbname || 'db'
    };
    const dbType = this.readDatabaseType(project);
    const connectionName = `${project.name} - DDEV`;

    return buildTablePlusUrl(getDbDriver(dbType), dbConfig, connectionName);
  }

  readDatabaseType(project) {
    try {
      const configPath = path.join(project.path, '.ddev', 'config.yaml');
      const config = yaml.load(fs.readFileSync(configPath, 'utf8')) || {};
      return config.database?.type || 'mysql';
    } catch (error) {
      return 'mysql';
    }
  }

  runtimeFromDescribe(project, raw) {
    const primaryUrl = raw.primary_url || project.preferredUrl;
    const status = String(raw.status || '').toLowerCase();
    const running = status === 'running' || raw.running === true;
    const port = this.resolveLocalPort(raw);

    return {
      projectId: project.id,
      running,
      localUrl: primaryUrl,
      localPort: port,
      exposed: false,
      health: running ? 'healthy' : 'stopped',
      lastSeenAt: new Date().toISOString()
    };
  }

  resolveLocalPort(raw) {
    if (raw.router_http_port) return Number(raw.router_http_port);
    if (raw.router_https_port) return Number(raw.router_https_port);
    if (raw.webserver_port) return Number(raw.webserver_port);
    if (raw.webimage_port) return Number(raw.webimage_port);
    return undefined;
  }
}

module.exports = { DdevProvider };
