<?php

require_once __DIR__ . "/AXML.php";
require_once __DIR__ . "/ARSC.php";
require_once __DIR__ . "/IconExtractor.php";

class ApkParser {

    private $zip;
    private $manifestXML;
    private $resources;
    private $apkFile;
    private $cacheFile;
    private $cachedData = null;

    public function __construct($apkFile) {
        $this->apkFile = $apkFile;

        // Ensure cache folder exists
        $cacheDir = __DIR__ . "/../cache";
        if (!is_dir($cacheDir)) {
            mkdir($cacheDir, 0777, true);
        }

        // Cache filename (hash-based)
        $hash = md5(basename($apkFile));
        $this->cacheFile = "$cacheDir/$hash.json";

        // Try loading cache
        if ($this->loadCache()) {
            return; // Cache loaded successfully
        }

        // No valid cache → parse APK normally
        $this->parseAPK();

        // Save cache
        $this->saveCache();
    }

    private function loadCache() {
        if (!file_exists($this->cacheFile)) {
            return false;
        }

        $json = json_decode(file_get_contents($this->cacheFile), true);
        if (!$json) {
            return false;
        }

        // Check if APK changed
        $apkMTime = filemtime($this->apkFile);
        $apkSize = filesize($this->apkFile);

        if ($json['mtime'] != $apkMTime || $json['size'] != $apkSize) {
            return false; // APK changed → invalidate cache
        }

        $this->cachedData = $json['data'];
        return true;
    }

    private function saveCache() {
        $data = [
            "mtime" => filemtime($this->apkFile),
            "size"  => filesize($this->apkFile),
            "data"  => $this->cachedData
        ];

        file_put_contents($this->cacheFile, json_encode($data, JSON_PRETTY_PRINT));
    }

    private function parseAPK() {
        $this->zip = new ZipArchive;
        if ($this->zip->open($this->apkFile) !== TRUE) {
            throw new Exception("Cannot open APK: $this->apkFile");
        }

        // Decode AndroidManifest.xml
        $binaryManifest = $this->zip->getFromName("AndroidManifest.xml");
        if (!$binaryManifest) {
            throw new Exception("Missing AndroidManifest.xml");
        }

        $axml = new AXMLParser();
        $this->manifestXML = $axml->decode($binaryManifest);

        // Parse resources.arsc
        $arscData = $this->zip->getFromName("resources.arsc");
        $this->resources = $arscData ? new ARSCParser($arscData) : null;

        // Extract metadata
        $xml = simplexml_load_string($this->manifestXML);

        $iconExtractor = new IconExtractor($this->zip, $this->resources);
        $iconRef = (string)$xml->application['icon'];
        $iconBase64 = $iconRef ? $iconExtractor->extractIconBase64($iconRef) : null;

        $permissions = [];
        foreach ($xml->{'uses-permission'} as $p) {
            $permissions[] = (string)$p['name'];
        }

        // Build cache data
        $this->cachedData = [
            "package"     => (string)$xml['package'],
            "versionName" => (string)$xml['versionName'],
            "versionCode" => (string)$xml['versionCode'],
            "minSdk"      => (string)$xml->{"uses-sdk"}['minSdkVersion'],
            "targetSdk"   => (string)$xml->{"uses-sdk"}['targetSdkVersion'],
            "appName"     => $this->resolveAppName($xml),
            "icon"        => $iconBase64,
            "permissions" => $permissions
        ];
    }

    private function resolveAppName($xml) {
        $label = (string)$xml->application['label'];

        if (strpos($label, "@") === 0 && $this->resources) {
            return $this->resources->resolveStringResource($label) ?: "Unknown App";
        }

        return $label ?: "Unknown App";
    }

    /* ---------------- Public API (reads from cache) ---------------- */

    public function getPackageName() {
        return $this->cachedData['package'];
    }

    public function getVersionName() {
        return $this->cachedData['versionName'];
    }

    public function getVersionCode() {
        return $this->cachedData['versionCode'];
    }

    public function getMinSdk() {
        return $this->cachedData['minSdk'];
    }

    public function getTargetSdk() {
        return $this->cachedData['targetSdk'];
    }

    public function getAppName() {
        return $this->cachedData['appName'];
    }

    public function getIconBase64() {
        return $this->cachedData['icon'];
    }

    public function getPermissions() {
        return $this->cachedData['permissions'];
    }

    public function getPermissionsDetailed() {
        $list = $this->cachedData['permissions'];
        $detailed = [];

        foreach ($list as $perm) {
            $detailed[] = [
                "technical" => $perm,
                "friendly"  => $this->friendlyPermissionName($perm)
            ];
        }

        return $detailed;
    }

    private function friendlyPermissionName($perm) {
        static $map = [
            "android.permission.INTERNET" => "Internet access",
            "android.permission.CAMERA" => "Camera access",
            "android.permission.ACCESS_FINE_LOCATION" => "Precise location",
            "android.permission.ACCESS_COARSE_LOCATION" => "Approximate location",
            "android.permission.READ_CONTACTS" => "Read contacts",
            "android.permission.WRITE_CONTACTS" => "Modify contacts",
            "android.permission.RECORD_AUDIO" => "Microphone access",
            "android.permission.READ_EXTERNAL_STORAGE" => "Read storage",
            "android.permission.WRITE_EXTERNAL_STORAGE" => "Write storage",
            "android.permission.BLUETOOTH" => "Bluetooth access",
            "android.permission.BLUETOOTH_ADMIN" => "Bluetooth control",
        ];

        return $map[$perm] ?? "General permission";
    }
}