<?php

namespace Modules\Admin\About\Model;

/**
 * Reads composer.lock files and extracts OSS library information including license text.
 */
class LicenseReader
{

	/**
	 * License file name patterns to search for (case-insensitive).
	 */
	protected const LICENSE_FILE_PATTERNS = [
		'LICENSE',
		'LICENSE.md',
		'LICENSE.txt',
		'LICENCE',
		'LICENCE.md',
		'LICENCE.txt',
		'COPYING',
		'COPYING.md',
		'COPYING.txt',
	];


	/**
	 * Loads and merges libraries from all registered sources.
	 * @param SourcesCollector $collector
	 * @return array - sorted, deduplicated list of library records
	 */
	public function loadFromSources(SourcesCollector $collector): array
	{
		$libraries = [];

		foreach ($collector->getSources() as $sourceEntry) {
			$parsed = $this->parseComposerLock($sourceEntry['composerLockPath'], $sourceEntry['vendorPath'], $sourceEntry['source']);
			foreach ($parsed as $lib) {
				$key = $lib['name'];
				if (isset($libraries[$key])) {
					// merge source labels
					if (strpos($libraries[$key]['source'], $lib['source']) === false) {
						$libraries[$key]['source'] .= ', ' . $lib['source'];
					}
				} else {
					$libraries[$key] = $lib;
				}
			}
		}

		// merge manually added libraries (non-PHP / non-composer)
		foreach ($collector->getManualLibraries() as $lib) {
			$key = $lib['name'];
			if (isset($libraries[$key])) {
				if (strpos($libraries[$key]['source'], $lib['source']) === false) {
					$libraries[$key]['source'] .= ', ' . $lib['source'];
				}
			} else {
				$libraries[$key] = $lib;
			}
		}

		// sort alphabetically by name
		ksort($libraries);

		return array_values($libraries);
	}


	/**
	 * Parses a single composer.lock file and returns library records.
	 * @param string $composerLockPath
	 * @param string $vendorPath
	 * @param string $sourceLabel
	 * @return array
	 */
	protected function parseComposerLock(string $composerLockPath, string $vendorPath, string $sourceLabel): array
	{
		if (!is_file($composerLockPath)) {
			return [];
		}

		$content = file_get_contents($composerLockPath);
		if ($content === false) {
			return [];
		}

		$data = json_decode($content, true);
		if (!is_array($data) || empty($data['packages'])) {
			return [];
		}

		$libraries = [];
		foreach ($data['packages'] as $package) {
			$name = $package['name'] ?? '';
			if (!$name) {
				continue;
			}

			$libraries[$name] = [
				'name'        => $name,
				'version'     => $package['version'] ?? '—',
				'license'     => !empty($package['license']) ? implode(', ', $package['license']) : '—',
				'description' => $package['description'] ?? '—',
				'url'         => $this->resolveUrl($package),
				'licenseText' => $this->readLicenseText($vendorPath, $name),
				'source'      => $sourceLabel,
			];
		}

		return $libraries;
	}


	/**
	 * Resolves the best URL for a package.
	 * Priority: homepage > support.source > null
	 * @param array $package
	 * @return string|null
	 */
	protected function resolveUrl(array $package): ?string
	{
		if (!empty($package['homepage'])) {
			return $package['homepage'];
		}
		if (!empty($package['support']['source'])) {
			return $package['support']['source'];
		}
		return null;
	}


	/**
	 * Reads the license text file from the vendor package directory.
	 * @param string $vendorPath
	 * @param string $packageName
	 * @return string
	 */
	protected function readLicenseText(string $vendorPath, string $packageName): string
	{
		$packageDir = rtrim($vendorPath, '/\\') . DIRECTORY_SEPARATOR . str_replace('/', DIRECTORY_SEPARATOR, $packageName);

		if (!is_dir($packageDir)) {
			return 'License text not available.';
		}

		// get all files in the package directory for case-insensitive matching
		$files = scandir($packageDir);
		if ($files === false) {
			return 'License text not available.';
		}

		$filesLower = array_combine(
			array_map('strtolower', $files),
			$files
		);

		foreach (self::LICENSE_FILE_PATTERNS as $pattern) {
			$patternLower = strtolower($pattern);
			if (isset($filesLower[$patternLower])) {
				$filePath = $packageDir . DIRECTORY_SEPARATOR . $filesLower[$patternLower];
				$text = file_get_contents($filePath);
				if ($text !== false) {
					return $text;
				}
			}
		}

		return 'License text not available.';
	}

}
