import { execFileSync } from 'node:child_process';
import * as fs from 'node:fs';
import * as path from 'node:path';
import { fileURLToPath } from 'node:url';
import { BangleConfig as LearnitiveConfig } from '@bangle.io/config-template';

const __dirname = path.dirname(fileURLToPath(import.meta.url));

const packageJson = JSON.parse(
  fs.readFileSync(
    path.join(__dirname, '..', '..', '..', 'package.json'),
    'utf-8',
  ),
);
const releaseVersion: string = packageJson.version;
const RELEASE_VERSION = process.env.LEARNITIVE_RELEASE_VERSION || releaseVersion;

const IS_CLOUDFLARE_PAGES = process.env.CF_PAGES === '1';
const IS_GITHUB_ACTIONS = process.env.GITHUB_ACTIONS === 'true';
const IS_CI_BUILD = IS_CLOUDFLARE_PAGES || IS_GITHUB_ACTIONS;

const GIT_BRANCH = process.env.CF_PAGES_BRANCH || process.env.GITHUB_REF_NAME;
const GIT_COMMIT_SHA =
  process.env.CF_PAGES_COMMIT_SHA || process.env.GITHUB_SHA;

const CI_BUILD_ID = GIT_COMMIT_SHA;

const LEARNITIVE_HOT_ENABLED = process.env.LEARNITIVE_HOT;
const APP_ENV_OVERRIDE = process.env.LEARNITIVE_APP_ENV?.trim();

function getCommitHash(): string {
  const configuredSha = GIT_COMMIT_SHA?.trim();
  if (configuredSha) {
    return configuredSha.slice(0, 7);
  }

  try {
    const repositoryRoot = path.join(__dirname, '..', '..', '..');
    const localSha = execFileSync('git', ['rev-parse', '--short', 'HEAD'], {
      cwd: repositoryRoot,
      encoding: 'utf8',
      stdio: ['ignore', 'pipe', 'ignore'],
    }).trim();
    if (localSha) {
      return localSha.slice(0, 7);
    }
  } catch {
    // Production ZIP/tarball deployments intentionally have no .git metadata.
  }

  return 'archive';
}

function readChangelogText(): string {
  return fs.readFileSync(
    path.join(__dirname, '..', '..', '..', 'CHANGELOG.md'),
    'utf-8',
  );
}

/**
 * @returns either `local`, `production`, `staging` or `dev/*` where * is the current branch name
 */
function getAppEnv(isProd: boolean): string {
  if (APP_ENV_OVERRIDE) {
    if (
      APP_ENV_OVERRIDE !== 'local' &&
      APP_ENV_OVERRIDE !== 'production' &&
      APP_ENV_OVERRIDE !== 'staging' &&
      !APP_ENV_OVERRIDE.startsWith('dev/')
    ) {
      throw new Error(`Invalid LEARNITIVE_APP_ENV: ${APP_ENV_OVERRIDE}`);
    }
    return APP_ENV_OVERRIDE;
  }

  if (!isProd || !IS_CI_BUILD) {
    return 'local';
  }

  if (GIT_BRANCH === 'production') {
    return 'production';
  }
  if (GIT_BRANCH === 'staging' || GIT_BRANCH === 'main') {
    return 'staging';
  }

  return `dev/${GIT_BRANCH}`;
}

function getReleaseId(isProduction: boolean): string {
  if (!isProduction) {
    return `${RELEASE_VERSION}+local`;
  }
  return `${RELEASE_VERSION}+${CI_BUILD_ID || getCommitHash()}`;
}

interface EnvVarsOptions {
  isProduction?: boolean;
  isStorybook?: boolean;
  helpDocsVersion?: string;
  inlinedScripts?: string[];
}

interface HtmlInjections {
  inlinedScripts: string;
  goatAnalytics: string;
}

interface GlobalIdentifiers {
  __LEARNITIVE_BUILD_TIME_CONFIG__: string;
}

interface EnvVarsResult {
  helpDocsVersion?: string;
  appEnv: string;
  hot: boolean;
  releaseId: string;
  htmlInjections: HtmlInjections;
  globalIdentifiers: GlobalIdentifiers;
}
export default ({
  isProduction = false,
  isStorybook = false,
  helpDocsVersion,
  inlinedScripts = [],
}: EnvVarsOptions): EnvVarsResult => {
  const appEnv = getAppEnv(isProduction);
  const releaseId = getReleaseId(isProduction);

  const hot = Boolean(LEARNITIVE_HOT_ENABLED);
  const learnitiveBuildConfig = new LearnitiveConfig({
    build: {
      appEnv: appEnv,
      buildTime: new Date().toISOString(),
      commitHash: getCommitHash(),
      deployBranch: isProduction ? GIT_BRANCH || appEnv : 'local',
      hot,
      netlifyBuildContext: IS_CLOUDFLARE_PAGES
        ? 'cloudflare_pages'
        : IS_GITHUB_ACTIONS
          ? 'github_actions'
          : 'local',
      nodeEnv: isProduction ? 'production' : 'development',
      releaseId,
      releaseVersion: RELEASE_VERSION,
      storybook: isStorybook,
    },
    app: {
      // The public /change-log is server/database-backed. Do not embed the
      // historical source changelog in production bundles; it contains retired
      // product-era release history and needlessly bloats the client config.
      changelogText: isProduction ? '' : readChangelogText(),
      helpDocsVersion,
    },
  });
  // No build-time analytics ID is embedded here. BF38 loads production analytics
  // through the route-aware runtime telemetry shell and trusted public config.
  const goatAnalytics = '';

  learnitiveBuildConfig.print();

  return {
    helpDocsVersion,
    appEnv,
    hot,
    releaseId,
    htmlInjections: {
      inlinedScripts: `
<script>
${inlinedScripts.join(';\n\n')}
</script>`.trim(),
      goatAnalytics,
    },
    globalIdentifiers: {
      __LEARNITIVE_BUILD_TIME_CONFIG__: JSON.stringify(learnitiveBuildConfig.serialize()),
    },
  };
};
