import { BaseAsset } from "@apic/studio-client-model";
import { checkForNullOrUndefined } from "../common/data-helper.js";
import AdmZip from "adm-zip";
import path from "node:path";
import fs from "node:fs";
import { showError, showInfo, showWarning } from "../common/message-helper.js";
import { AssetCacheModel } from "../../model/asset-cache-model.js";
import { APIAsset } from "../../model/assets-model.js";

const bundleApiDependency = (
    asset: BaseAsset,
    searchResult: fs.Dirent<string>,
    cachedUnProcessedAsset: AssetCacheModel,
    rootDirPath: string,
    project: string,
    zipFile: AdmZip
) => {
    const sourceProjectName = cachedUnProcessedAsset.sourceProject;

    if (!sourceProjectName) {
        showError(`Source project not found for API dependency ${cachedUnProcessedAsset.ref}`);
        return;
    }

    // Pass the timestamp as the unique value that gets appended to project name
    const timeStamp = Date.now();

    // Add API with project folder structure
    const apiRelativePath = addApiDependencyAsset(
        searchResult,
        zipFile,
        project,    // target project name
        sourceProjectName,
        rootDirPath,
        timeStamp
    );
    showInfo(`API added: ${project}/${apiRelativePath}`);


    // Also add the API specification
    resolveAndAddApiSpec(
        asset,
        zipFile,
        apiRelativePath,
        project,    // target project name
        sourceProjectName,
        rootDirPath,
        timeStamp
    );
}

const addApiDependencyAsset = (
    file: fs.Dirent,
    zip: AdmZip,
    targetProjectName: string,
    sourceProjectName: string,
    rootDirPath: string,
    timeStamp: number
) => {
    // Calculate relative path from target project root
    const targetProjectPath = path.join(rootDirPath, targetProjectName);
    const relativePath = path.relative(targetProjectPath, path.join(file.parentPath, file.name));

    // Add to zip with project folder structure nested inside source project
    // Result: sourceProject/targetProject/api-assets/api.yml
    const zipPath = path.join(sourceProjectName, `${targetProjectName}_${timeStamp}`, relativePath);
    zip.addLocalFile(
        path.join(file.parentPath, file.name),
        path.dirname(zipPath)
    );

    return relativePath;
};

const resolveAndAddApiSpec = (
    asset: BaseAsset,
    zip: AdmZip,
    apiFileRelativePath: string,
    targetProjectName: string,
    sourceProjectName: string,
    rootDirPath: string,
    timeStamp: number
) => {
    const apiAsset = asset as unknown as APIAsset;
    const spec = checkForNullOrUndefined(
        apiAsset.spec,
        `Spec is not defined for the asset with kind 'API' and name '${apiAsset.metadata?.name}'`
    );
    const apiSpec = checkForNullOrUndefined(
        spec["api-spec"],
        `Attribute 'api-spec' is not defined
	for kind 'API' and name '${apiAsset.metadata?.name}'`
    );
    const apiSpecPath = checkForNullOrUndefined(
        apiSpec.$path,
        `API Definition Path is not found for ${asset}`
    );

    // Get the directory where the API file is located
    const apiFileDir = path.dirname(apiFileRelativePath);

    // Resolve api spec path relative to project folder by default
    let resolvedSpecPath = apiSpecPath;

    if (apiSpecPath.startsWith('../') || apiSpecPath.startsWith('./')) {
        // Resolve relative path from API file's directory
        // Example: apiFileDir = "api-assets", apiSpecPath = "../specs/petstore.yaml"
        // Result: "specs/petstore.yaml"
        const specPathFromProjectRoot = path.join(apiFileDir, apiSpecPath);
        resolvedSpecPath = path.normalize(specPathFromProjectRoot);
    }

    // Build absolute file system path for reading the spec
    const absoluteSpecPath = path.join(rootDirPath, targetProjectName, resolvedSpecPath);

    if (!fs.existsSync(absoluteSpecPath)) {
        showWarning(
            `API spec not found: ${absoluteSpecPath} for API ${apiAsset.metadata?.namespace}:${apiAsset.metadata?.name}:${apiAsset.metadata.version}`
        );
        return;
    }

    // Build zip path using the relative spec path from API
    // apiSpecPath is already relative (e.g., "specs/petstore.yaml")
    const zipSpecPath = path.join(sourceProjectName, `${targetProjectName}_${timeStamp}`, resolvedSpecPath);

    zip.addLocalFile(absoluteSpecPath, path.dirname(zipSpecPath));
    showInfo(`Spec added: ${zipSpecPath}`);
};

export { bundleApiDependency, addApiDependencyAsset, resolveAndAddApiSpec }