const fs = require('fs');
const path = require('path');
const axios = require('axios');
const { execFileSync } = require('child_process');
const fse = require('fs-extra');

function fixTypeName(name) {
  return name.replace(/\s+/g, '');
}

function fixSwagger(doc) {
  if (Array.isArray(doc.servers)) {
    doc.servers = [{ url: '' }];
  }
  if (doc.components && doc.components.schemas) {
    for (const [key, schema] of Object.entries(doc.components.schemas)) {
      if (key.includes(' ')) {
        const newKey = fixTypeName(key);
        doc.components.schemas[newKey] = schema;
        delete doc.components.schemas[key];
      }
    }
  }
  if (doc.paths) {
    for (const [_, methods] of Object.entries(doc.paths)) {
      for (const [_, method] of Object.entries(methods)) {
        if (method.parameters) {
          method.parameters = method.parameters
            .filter((p) => p.in !== 'header')
            .map((p) => {
              if (p.type) {
                p.schema = { type: fixTypeName(p.type) };
                if (p.format) p.schema.format = p.format;
                delete p.type;
                delete p.format;
              }
              if (p.schema && p.schema.$ref) {
                p.schema.$ref = p.schema.$ref.replace(/\s+/g, '');
              }
              return p;
            });
        }
        if (method.requestBody && method.requestBody.content) {
          for (const contentType in method.requestBody.content) {
            const schema = method.requestBody.content[contentType].schema;
            if (schema && schema.$ref) {
              schema.$ref = schema.$ref.replace(/\s+/g, '');
            }
          }
        }
        if (method.responses) {
          for (const code in method.responses) {
            const resp = method.responses[code];
            if (resp.content) {
              for (const contentType in resp.content) {
                const schema = resp.content[contentType].schema;
                if (schema && schema.$ref) {
                  schema.$ref = schema.$ref.replace(/\s+/g, '');
                }
              }
            }
          }
        }
        if (method.operationId && typeof method.operationId === 'string') {
          const parts = method.operationId.split('/');
          method.operationId = parts[parts.length - 1];
        }
      }
    }
  }
  return doc;
}

async function fetchAndFixSwagger(url, outputPath) {
  const { data } = await axios.get(url);
  const fixed = fixSwagger(data);
  fs.writeFileSync(outputPath, JSON.stringify(fixed, null, 2), 'utf-8');
}

function quoteForCmd(arg) {
  if (arg === '') return '""';
  const needsQuote = /[\s&()^\[\]{}=;!'+,`~|<>"]/g.test(arg);
  if (!needsQuote) return arg;
  return `"${String(arg).replace(/"/g, '\\"')}"`;
}

function execOnWindowsViaCmd(commandLine) {
  execFileSync('cmd.exe', ['/d', '/s', '/c', commandLine], { stdio: 'inherit' });
}

function runAndReport(cmd, args, options = {}) {
  try {
    execFileSync(cmd, args, options);
  } catch (e) {
    const stdout = e && e.stdout ? e.stdout.toString() : '';
    const stderr = e && e.stderr ? e.stderr.toString() : '';
    if (stdout.trim()) console.error(`\n[openapi-generator stdout]\n${stdout}\n`);
    if (stderr.trim()) console.error(`\n[openapi-generator stderr]\n${stderr}\n`);
    throw e;
  }
}

/**
 * 如果你不想改系统环境变量：
 * 1) 安装 JDK 后，改这里的路径为你的 JDK 安装目录
 * 2) 脚本会临时把 JAVA_HOME/bin 注入 PATH，只影响本次运行
 */
function withJavaEnv(baseEnv) {
  const env = { ...baseEnv };

  // ✅ 如果你已全局装好 Java（java -version 可用），可以把这块注释掉
  // TODO: 把这里改成你的 JDK 路径
  const JDK_HOME = env.JDK_HOME || env.JAVA_HOME || 'C:\\Program Files\\Java\\jdk-17';

  if (process.platform === 'win32') {
    const javaBin = path.join(JDK_HOME, 'bin');
    env.JAVA_HOME = JDK_HOME;
    env.PATH = `${javaBin};${env.PATH || ''}`;
  } else {
    // Unix: 一般装好后 java 已在 PATH；如需也可注入
    // env.JAVA_HOME = JDK_HOME;
    // env.PATH = `${path.join(JDK_HOME, 'bin')}:${env.PATH || ''}`;
  }

  return env;
}

function runOpenapiGenerator(args) {
  const localCliMain = path.resolve(
    process.cwd(),
    'node_modules',
    '@openapitools',
    'openapi-generator-cli',
    'main.js'
  );

  const env = withJavaEnv(process.env);

  if (fs.existsSync(localCliMain)) {
    runAndReport(process.execPath, [localCliMain, ...args], {
      stdio: ['ignore', 'pipe', 'pipe'],
      env,
    });
    return;
  }

  if (process.platform === 'win32') {
    const cmdline = ['npx', 'openapi-generator-cli', ...args]
      .map(quoteForCmd)
      .join(' ');
    execFileSync('cmd.exe', ['/d', '/s', '/c', cmdline], { stdio: 'inherit', env });
    return;
  }

  runAndReport('npx', ['openapi-generator-cli', ...args], {
    stdio: ['ignore', 'pipe', 'pipe'],
    env,
  });
}

function generateClient(inputPath, outputDir) {
  const relativeInputPath = path.relative(process.cwd(), inputPath);
  const relativeOutputDir = path.relative(process.cwd(), outputDir);

  try {
    runOpenapiGenerator([
      'generate',
      '--skip-validate-spec',
      '-i',
      relativeInputPath,
      '-g',
      'typescript-axios',
      '-o',
      relativeOutputDir,
    ]);
  } catch (e) {
    const msg = e && e.message ? e.message : String(e);
    console.error(`❌ openapi-generator 执行失败: ${msg}`);
    throw e;
  }
}

function moveToSrcApi(serviceName, tempDir, targetBase) {
  const destDir = path.join(targetBase, serviceName);
  if (fs.existsSync(destDir)) fse.removeSync(destDir);

  const filesToRemove = [
    '.swagger-codegen',
    '.gitignore',
    '.npmignore',
    '.swagger-codegen-ignore',
    'git_push.sh',
    'package.json',
    'README.md',
    'tsconfig.json',
  ];
  for (const file of filesToRemove) {
    const f = path.join(tempDir, file);
    if (fs.existsSync(f)) fs.rmSync(f, { recursive: true, force: true });
  }

  fse.moveSync(tempDir, destDir, { overwrite: true });
  console.log(`✅ ${serviceName} 接口生成完毕，已移动到 src/api/${serviceName}`);
}

function toCamelExportName(serviceName) {
  const parts = serviceName.split(/[^a-zA-Z0-9]+/).filter(Boolean);
  if (parts.length === 0) return serviceName;
  const [first, ...rest] = parts;
  return (
    first.toLowerCase() +
    rest.map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join('')
  );
}

function toEnvKey(serviceName) {
  return serviceName.replace(/[^a-zA-Z0-9]+/g, '_').toUpperCase();
}

function writeServiceClient(serviceName, targetBase) {
  const destDir = path.join(targetBase, serviceName);
  const envKey = `VITE_${toEnvKey(serviceName)}_API_BASE`;
  const content = `import { Configuration } from "@/api/${serviceName}/configuration";\nimport http from "@/utils/http";\n\nconst env = import.meta.env as Record<string, string | undefined>;\nconst apiBase = (env[\"${envKey}\"] ?? import.meta.env.VITE_API_BASE ?? \"\").replace(/\\/+$/, \"\");\nconst configuration = new Configuration({\n  basePath: apiBase,\n});\n\ntype ApiConstructor<T> = new (\n  config?: Configuration,\n  basePath?: string,\n  axios?: typeof http,\n) => T;\n\nexport const createApi = <T>(ApiClass: ApiConstructor<T>): T =>\n  new ApiClass(configuration, undefined, http);\n\nexport const apiConfig = configuration;\nexport { http };\n`;
  fs.writeFileSync(path.join(destDir, 'client.ts'), content, 'utf-8');
}

function updateApiIndex(services, targetBase) {
  const lines = services.map(
    (svc) =>
      `export * as ${toCamelExportName(svc.name)} from "@/api/${svc.name}/client";`
  );
  const content = `${lines.join('\n')}\n`;
  fs.writeFileSync(path.join(targetBase, 'index.ts'), content, 'utf-8');
}

async function generateService(serviceName, apiUrl) {
  const tempPath = path.join(__dirname, '.tmp', serviceName);
  const fixedJsonPath = path.join(tempPath, 'swagger-fixed.json');

  fs.mkdirSync(tempPath, { recursive: true });
  await fetchAndFixSwagger(apiUrl, fixedJsonPath);
  generateClient(fixedJsonPath, tempPath);
  const apiBaseDir = path.resolve(__dirname, '../src/api');
  moveToSrcApi(serviceName, tempPath, apiBaseDir);
  writeServiceClient(serviceName, apiBaseDir);
}

async function main() {
  const base = 'http://10.20.124.50:30030';
  const services = [
    { name: 'userservice', url: `${base}/userservice/v2/v3/api-docs` },
    { name: 'storageservice', url: `${base}/storageservice/v2/v3/api-docs` },
    { name: 'underwatersys', url: `${base}/underwatersys/v3/api-docs` },
  ];

  for (const svc of services) {
    try {
      await generateService(svc.name, svc.url);
    } catch (err) {
      console.error(`❌ 生成失败: ${svc.name}`, err && err.message ? err.message : err);
    }
  }

  updateApiIndex(services, path.resolve(__dirname, '../src/api'));
}

main();
