const fs = require('fs');
const path = require('path');
const YAML = require('yaml');

const repoRoot = process.cwd();
const rootFile = path.join(repoRoot, 'pocket-base', 'spec', 'openapi-wx.yaml');
const splitDir = path.join(repoRoot, 'pocket-base', 'spec', 'openapi-wx');

const filePaths = [
  rootFile,
  ...fs
    .readdirSync(splitDir)
    .filter((name) => name.endsWith('.yaml'))
    .map((name) => path.join(splitDir, name)),
];

const docs = new Map(
  filePaths.map((filePath) => [filePath, YAML.parse(fs.readFileSync(filePath, 'utf8'))]),
);

function decodePointerSegment(segment) {
  return segment.replace(/~1/g, '/').replace(/~0/g, '~');
}

function getByPointer(documentData, pointer) {
  if (!pointer || pointer === '#' || pointer === '') {
    return documentData;
  }

  const segments = pointer
    .replace(/^#/, '')
    .split('/')
    .filter(Boolean)
    .map(decodePointerSegment);

  let current = documentData;
  for (const segment of segments) {
    if (current == null) {
      return undefined;
    }
    current = current[segment];
  }
  return current;
}

function resolveRef(ref, currentFile) {
  const [filePart, hashPart = ''] = ref.split('#');
  const targetFile = filePart
    ? path.resolve(path.dirname(currentFile), filePart)
    : currentFile;
  const targetDoc = docs.get(targetFile);
  if (!targetDoc) {
    return { targetFile, targetKey: `${targetFile}#${hashPart}`, schema: undefined };
  }

  const pointer = hashPart ? `#${hashPart}` : '#';
  return {
    targetFile,
    targetKey: `${targetFile}${pointer}`,
    schema: getByPointer(targetDoc, pointer),
  };
}

function pickType(typeValue) {
  if (Array.isArray(typeValue)) {
    return typeValue.find((item) => item !== 'null') || typeValue[0];
  }
  return typeValue;
}

function cleanLabelText(text, fallback) {
  if (!text || typeof text !== 'string') {
    return fallback;
  }

  const normalized = text
    .split(/\r?\n/)
    .map((line) => line.trim())
    .filter(Boolean)
    .map((line) => line.replace(/^[\-\d\.\)\s]+/, '').trim())
    .find((line) => line && !/^(可选|必填|说明|注意)[。:：]?$/u.test(line));

  if (!normalized) {
    return fallback;
  }

  return normalized.replace(/[`"'<>]/g, '').trim() || fallback;
}

function placeholderKey(key) {
  return typeof key === 'string' && key.includes('|');
}

function deepMerge(baseValue, nextValue) {
  if (
    baseValue &&
    nextValue &&
    typeof baseValue === 'object' &&
    typeof nextValue === 'object' &&
    !Array.isArray(baseValue) &&
    !Array.isArray(nextValue)
  ) {
    const merged = { ...baseValue };
    const nextKeys = Object.keys(nextValue);
    const hasConcreteNextKeys = nextKeys.some((key) => !placeholderKey(key));

    if (hasConcreteNextKeys) {
      for (const key of Object.keys(merged)) {
        if (placeholderKey(key)) {
          delete merged[key];
        }
      }
    }

    for (const [key, value] of Object.entries(nextValue)) {
      if (key in merged) {
        merged[key] = deepMerge(merged[key], value);
      } else {
        merged[key] = value;
      }
    }
    return merged;
  }
  return nextValue;
}

function buildLabel(schemaLike, fallback) {
  return cleanLabelText(schemaLike?.description || schemaLike?.title, fallback);
}

function fallbackScalar(label, fieldType) {
  return `${label}|${fieldType || 'string'}`;
}

function buildExample(schemaLike, currentFile, fieldName = 'field', seenRefs = new Set()) {
  if (schemaLike == null) {
    return fallbackScalar(fieldName, 'string');
  }

  if (typeof schemaLike !== 'object' || Array.isArray(schemaLike)) {
    return fallbackScalar(fieldName, 'string');
  }

  if (schemaLike.$ref) {
    const { targetFile, targetKey, schema } = resolveRef(schemaLike.$ref, currentFile);
    if (!schema) {
      return fallbackScalar(fieldName, 'string');
    }
    if (seenRefs.has(targetKey)) {
      return fallbackScalar(fieldName, 'object');
    }
    const nextSeen = new Set(seenRefs);
    nextSeen.add(targetKey);
    return buildExample(schema, targetFile, fieldName, nextSeen);
  }

  if (schemaLike.allOf) {
    return schemaLike.allOf.reduce((accumulator, item) => {
      const nextValue = buildExample(item, currentFile, fieldName, seenRefs);
      return deepMerge(accumulator, nextValue);
    }, {});
  }

  if (schemaLike.oneOf && schemaLike.oneOf.length > 0) {
    return buildExample(schemaLike.oneOf[0], currentFile, fieldName, seenRefs);
  }

  if (schemaLike.anyOf && schemaLike.anyOf.length > 0) {
    return buildExample(schemaLike.anyOf[0], currentFile, fieldName, seenRefs);
  }

  const fieldType =
    pickType(schemaLike.type) ||
    (schemaLike.properties || schemaLike.additionalProperties ? 'object' : undefined) ||
    (schemaLike.items ? 'array' : undefined) ||
    'string';
  const fieldLabel = buildLabel(schemaLike, fieldName);

  if (fieldType === 'object') {
    const exampleObject = {};

    if (schemaLike.properties && typeof schemaLike.properties === 'object') {
      for (const [propertyName, propertySchema] of Object.entries(schemaLike.properties)) {
        exampleObject[propertyName] = buildExample(
          propertySchema,
          currentFile,
          propertyName,
          seenRefs,
        );
      }
    }

    if (Object.keys(exampleObject).length === 0 && schemaLike.additionalProperties) {
      exampleObject[`${fieldLabel}字段|string`] = `${fieldLabel}值|string`;
    }

    if (Object.keys(exampleObject).length === 0) {
      exampleObject[`${fieldLabel}|string`] = `${fieldLabel}|string`;
    }

    return exampleObject;
  }

  if (fieldType === 'array') {
    const itemFieldName = fieldName.endsWith('s') ? fieldName.slice(0, -1) : `${fieldName}_item`;
    return [buildExample(schemaLike.items || {}, currentFile, itemFieldName, seenRefs)];
  }

  return fallbackScalar(fieldLabel, fieldType);
}

function httpMethods(pathItem) {
  return ['get', 'post', 'put', 'patch', 'delete', 'options', 'head', 'trace'].filter(
    (method) => pathItem && typeof pathItem[method] === 'object',
  );
}

for (const filePath of filePaths.filter((file) => file !== rootFile)) {
  const documentData = docs.get(filePath);
  if (!documentData || typeof documentData !== 'object') {
    continue;
  }

  if (documentData.components && documentData.components.schemas) {
    for (const [schemaName, schemaValue] of Object.entries(documentData.components.schemas)) {
      schemaValue.example = buildExample(schemaValue, filePath, schemaName, new Set());
    }
  }

  if (documentData.paths) {
    for (const pathItem of Object.values(documentData.paths)) {
      for (const method of httpMethods(pathItem)) {
        const operation = pathItem[method];

        const requestContent = operation?.requestBody?.content;
        if (requestContent && typeof requestContent === 'object') {
          for (const media of Object.values(requestContent)) {
            if (media && media.schema) {
              delete media.examples;
              media.example = buildExample(media.schema, filePath, 'request', new Set());
            }
          }
        }

        const responses = operation?.responses;
        if (responses && typeof responses === 'object') {
          for (const response of Object.values(responses)) {
            const responseContent = response?.content;
            if (!responseContent || typeof responseContent !== 'object') {
              continue;
            }

            for (const media of Object.values(responseContent)) {
              if (media && media.schema) {
                delete media.examples;
                media.example = buildExample(media.schema, filePath, 'response', new Set());
              }
            }
          }
        }
      }
    }
  }

  fs.writeFileSync(filePath, YAML.stringify(documentData, { lineWidth: 0 }), 'utf8');
}

console.log(`Updated examples in ${filePaths.length - 1} split OpenAPI files.`);
