local LrDialogs = import "LrDialogs"
local LrFileUtils = import "LrFileUtils"
local L = require "L10n"
local LrApplication = import "LrApplication"
local LrPathUtils = import "LrPathUtils"
local LrPrefs = import "LrPrefs"
local LrTasks = import "LrTasks"

local function isPolishLocale()
    local ok, locale = pcall(function()
        return LrApplication.applicationUILocale()
    end)
    if not ok then return false end
    return tostring(locale or ""):lower():match("^pl") ~= nil
end

local UI_PL = isPolishLocale()
local function tr(plText, enText)
    if UI_PL then return plText end
    return enText
end

local function pluginLoad(relativePath)
    return dofile(LrPathUtils.child(_PLUGIN.path, relativePath))
end

local config = pluginLoad("lib/FilmEngineConfig.lua")
local logger = pluginLoad("lib/Logger.lua")
local profileInstaller = pluginLoad("lib/ProfileInstaller.lua")
local presetInstaller = pluginLoad("lib/PresetInstaller.lua")
local licenseClient = pluginLoad("lib/LicenseEntitlementClient.lua")

local VERSION_GUARD_FILES = {
    "Info.lua",
    "AnalogSignaturePanelOneMenu.lua",
    "lib/EngineBridge.lua",
    "lib/FilmEngineConfig.lua",
    "lib/Entitlement.lua",
    "lib/PublicCatalog.lua",
    "L10n.lua",
}

local function pluginVersionFromInfoFile()
    local path = LrPathUtils.child(_PLUGIN.path, "Info.lua")
    local f = io.open(path, "r")
    if not f then
        return {}
    end
    local text = f:read("*a") or ""
    f:close()
    local block = text:match("VERSION%s*=%s*%{(.-)%}") or ""
    return {
        major = tonumber(block:match("major%s*=%s*(%d+)")) or 0,
        minor = tonumber(block:match("minor%s*=%s*(%d+)")) or 0,
        revision = tonumber(block:match("revision%s*=%s*(%d+)")) or 0,
        build = tonumber(block:match("build%s*=%s*(%d+)")) or 0,
    }
end

local function pluginVersionTable()
    local version = (_PLUGIN and _PLUGIN.INFO and _PLUGIN.INFO.VERSION) or {}
    if tonumber(version.major or 0) ~= 0
        or tonumber(version.minor or 0) ~= 0
        or tonumber(version.revision or 0) ~= 0
        or tonumber(version.build or 0) ~= 0
    then
        return version
    end
    return pluginVersionFromInfoFile()
end

local function pluginVersionString()
    local version = pluginVersionTable()
    local major = tonumber(version.major or 0) or 0
    local minor = tonumber(version.minor or 0) or 0
    local revision = tonumber(version.revision or 0) or 0
    local build = tonumber(version.build or 0) or 0
    return string.format("%d.%d.%d.%d", major, minor, revision, build)
end

local function computeVersionGuardFingerprint()
    local parts = {}
    for _, relativePath in ipairs(VERSION_GUARD_FILES) do
        local absPath = LrPathUtils.child(_PLUGIN.path, relativePath)
        local attrs = LrFileUtils.fileAttributes(absPath) or {}
        local size = tonumber(attrs.fileSize or 0) or 0
        local mtime = tonumber(attrs.fileModificationDate or 0) or 0
        parts[#parts + 1] = relativePath .. ":" .. tostring(size) .. ":" .. tostring(mtime)
    end
    return table.concat(parts, "|")
end

local function runVersionGuard()
    local prefs = LrPrefs.prefsForPlugin()
    local currentVersion = pluginVersionString()
    local currentFingerprint = computeVersionGuardFingerprint()
    local previousVersion = tostring(prefs.versionGuardLastVersion or "")
    local previousFingerprint = tostring(prefs.versionGuardLastFingerprint or "")
    local warnedFingerprint = tostring(prefs.versionGuardWarnedFingerprint or "")

    if previousVersion ~= "" and previousVersion == currentVersion
        and previousFingerprint ~= "" and previousFingerprint ~= currentFingerprint
        and warnedFingerprint ~= currentFingerprint
    then
        prefs.versionGuardWarnedFingerprint = currentFingerprint
        logger.warn("Version guard: code changed without version bump", {
            version = currentVersion,
        })
        LrDialogs.message(
            "Analog Signature — Version Reminder",
            tr("Wykryto zmianę kodu wtyczki, ale VERSION nadal wynosi ", "Plugin code changed, but VERSION is still ")
                .. currentVersion
                .. tr(".\nPodbij Info.lua VERSION (minimum: build) przed wydaniem.", ".\nBump Info.lua VERSION (at least build) before release."),
            "warning"
        )
    end

    if previousVersion ~= currentVersion then
        prefs.versionGuardWarnedFingerprint = ""
    end
    prefs.versionGuardLastVersion = currentVersion
    prefs.versionGuardLastFingerprint = currentFingerprint
end

local function trim(s)
    return tostring(s or ""):gsub("^%s+", ""):gsub("%s+$", "")
end

local function readRetailLicenseLine(pluginPath)
    local path = LrPathUtils.child(pluginPath, "RetailLicense.txt")
    local f = io.open(path, "r")
    if not f then
        return nil
    end
    local body = f:read("*a") or ""
    f:close()
    local line = trim((body:match("^[^\r\n]+")) or "")
    if line == "" then
        return nil
    end
    return line
end

local function safeGetEnv(name)
    if type(os) == "table" and type(os.getenv) == "function" then
        return os.getenv(name)
    end
    return nil
end

local function buildDeviceId()
    local user = tostring(safeGetEnv("USER") or safeGetEnv("USERNAME") or "unknown")
    
    -- Try to get system serial number on macOS
    local serial = "unknown-serial"
    local status, output = pcall(function()
        -- Redirect stderr to /dev/null to avoid cluttering logs
        local cmd = "ioreg -rd1 -c IOPlatformExpertDevice | grep -E '(UUID|serial-number)' 2>/dev/null"
        -- Use io.popen since LrTasks.execute might be limited in capturing output directly in some contexts, 
        -- but here we are in PluginInit, so we use a temp file pattern if needed.
        -- Actually, LrTasks.execute returns status, we need output.
        local tempDir = LrPathUtils.getStandardFilePath("temp")
        local outPath = LrPathUtils.child(tempDir, "ml_hwid.txt")
        os.execute(cmd .. " > " .. outPath)
        local f = io.open(outPath, "r")
        if f then
            local raw = f:read("*a")
            f:close()
            os.remove(outPath)
            -- Extract serial number: "IOPlatformSerialNumber" = "C02XXXXX"
            local s = raw:match("\"IOPlatformSerialNumber\" = \"([^\"]+)\"")
            if s then return s end
            -- Fallback to UUID
            local u = raw:match("\"IOPlatformUUID\" = \"([^\"]+)\"")
            if u then return u end
        end
        return "unknown-hw"
    end)
    if status and output then serial = output end

    return user .. "@" .. serial
end

_G.ML_RETAIL_LICENSED = false
local okRetail, retailMod = pcall(function()
    return pluginLoad("lib/RetailLicense.lua")
end)
if okRetail and retailMod and type(retailMod.evaluate) == "function" then
    local licensed, reason = retailMod.evaluate(_PLUGIN.path)
    _G.ML_RETAIL_LICENSED = licensed == true
    logger.info("Retail license", {
        licensed = tostring(_G.ML_RETAIL_LICENSED),
        reason = tostring(reason or ""),
    })
end

_G.ML_ENTITLEMENT_STATUS = "legacy_offline_only"
_G.ML_ENTITLEMENT_REASON = ""

LrTasks.startAsyncTask(function()
    local baseUrl = trim(safeGetEnv("ML_LICENSE_API_URL") or "")
    if baseUrl == "" then
        logger.info("License entitlement online check skipped", {
            reason = "no_api_url",
        })
        return
    end
    if _G.ML_RETAIL_LICENSED ~= true then
        logger.info("License entitlement online check skipped", {
            reason = "retail_license_not_verified",
        })
        return
    end

    local licenseKey = readRetailLicenseLine(_PLUGIN.path)
    
    -- Prompt user if license is missing or invalid
    if _G.ML_RETAIL_LICENSED ~= true then
        LrTasks.startAsyncTask(function()
            local button = LrDialogs.confirm(
                tr("Mindful Lens — Brak aktywnej licencji", "Mindful Lens — No active license"),
                tr("Twoja licencja jest nieaktywna lub nieprawidłowa. Czy chcesz wprowadzić klucz licencyjny?", "Your license is inactive or invalid. Do you want to enter a license key?"),
                tr("Wprowadź klucz", "Enter key"),
                tr("Anuluj", "Cancel")
            )
            if button == "ok" then
                local f = import "LrView".osFactory()
                local c = f:column {
                    spacing = f:control_spacing(),
                    f:static_text {
                        title = tr("Wprowadź klucz (format: ASIG-RETAIL-XXXX-XX):", "Enter key (format: ASIG-RETAIL-XXXX-XX):"),
                    },
                    f:edit_field {
                        value = "",
                        immediate = true,
                        width_in_chars = 40,
                        binding = "ml_key_input",
                    },
                }
                local res = LrDialogs.presentModalDialog({
                    title = tr("Aktywacja Mindful Lens", "Mindful Lens Activation"),
                    contents = c,
                })
                if res == "ok" then
                    local newKey = trim(c.ml_key_input or "")
                    if newKey ~= "" then
                        local path = LrPathUtils.child(_PLUGIN.path, "RetailLicense.txt")
                        local fh = io.open(path, "w")
                        if fh then
                            fh:write(newKey)
                            fh:close()
                            LrDialogs.message(
                                tr("Sukces", "Success"),
                                tr("Klucz został zapisany. Zrestartuj Lightroom, aby aktywować plugin.", "Key saved. Restart Lightroom to activate the plugin."),
                                "info"
                            )
                        end
                    end
                end
            end
        end)
        return
    end

    if not licenseKey then
        _G.ML_ENTITLEMENT_STATUS = "missing_retail_license_line"
        _G.ML_ENTITLEMENT_REASON = "missing_retail_license_line"
        logger.warn("License entitlement skipped", {
            reason = _G.ML_ENTITLEMENT_REASON,
        })
        return
    end

    local appInfo = {
        product = "analog-signature-lr",
        major = 1,
        build = tonumber((pluginVersionTable() or {}).build or 0) or 0,
        platform = "macos",
    }
    local deviceId = buildDeviceId()

    local okCache, _, cached = licenseClient.loadCachedEntitlement()
    if okCache and type(cached) == "table" and type(cached.licenseId) == "string" then
        local tokenValue = (((cached or {}).token or {}).value)
        if type(tokenValue) == "string" and tokenValue ~= "" then
            local okRefresh, errRefresh, refreshRes = licenseClient.refresh(
                baseUrl, cached.licenseId, deviceId, tokenValue,
                { app = appInfo, timeoutSec = 6 }
            )
            if okRefresh and type(refreshRes) == "table" then
                licenseClient.saveCachedEntitlement(refreshRes)
                _G.ML_ENTITLEMENT_STATUS = "online_refreshed"
                _G.ML_ENTITLEMENT_REASON = ""
                logger.info("License entitlement refreshed", {
                    status = _G.ML_ENTITLEMENT_STATUS,
                    licenseId = tostring(refreshRes.licenseId or ""),
                })
                return
            end
            logger.warn("License entitlement refresh failed", {
                error = tostring(errRefresh or ""),
            })
        end
    end

    local okActivate, errActivate, activateRes = licenseClient.activate(
        baseUrl, licenseKey, deviceId, { app = appInfo, timeoutSec = 6 }
    )
    if okActivate and type(activateRes) == "table" then
        licenseClient.saveCachedEntitlement(activateRes)
        _G.ML_ENTITLEMENT_STATUS = "online_activated"
        _G.ML_ENTITLEMENT_REASON = ""
        logger.info("License entitlement activated", {
            status = _G.ML_ENTITLEMENT_STATUS,
            licenseId = tostring(activateRes.licenseId or ""),
        })
        return
    end

    -- fallback to cached entitlement if online activate failed
    local cacheFallbackOk = false
    if okCache and type(cached) == "table" then
        cacheFallbackOk = true
    end
    if cacheFallbackOk then
        _G.ML_ENTITLEMENT_STATUS = "offline_cached"
        _G.ML_ENTITLEMENT_REASON = tostring(errActivate or "activate_failed")
        logger.warn("License entitlement using cached fallback", {
            status = _G.ML_ENTITLEMENT_STATUS,
            reason = _G.ML_ENTITLEMENT_REASON,
        })
    else
        _G.ML_ENTITLEMENT_STATUS = "online_failed_no_cache"
        _G.ML_ENTITLEMENT_REASON = tostring(errActivate or "activate_failed")
        logger.error("License entitlement failed", {
            status = _G.ML_ENTITLEMENT_STATUS,
            reason = _G.ML_ENTITLEMENT_REASON,
        })
    end
end)

logger.info("Plugin initialized", {
    pluginPath = _PLUGIN.path,
    tempDir = LrPathUtils.getStandardFilePath("temp"),
})

runVersionGuard()

local ok, result = pcall(function()
    return profileInstaller.installAllDcpProfiles(_PLUGIN.path)
end)

if ok and result then
    logger.info("Plugin init DCP sync", {
        target = tostring(result.target),
        installed = tostring(result.installed),
        skipped = tostring(result.skipped),
        missing = tostring(result.missing),
        errors = tostring(result.errors),
        legacy_found = tostring(result.legacy_found or 0),
        legacy_removed = tostring(result.legacy_removed or 0),
        caches_deleted = tostring(result.caches_deleted or 0),
        legacy_errors = tostring(result.legacy_errors or 0),
        cache_errors = tostring(result.cache_errors or 0),
    })
    if (result.legacy_removed or 0) > 0 or (result.caches_deleted or 0) > 0 then
        local lines = {
            tr("Analog Signature — migracja starego stanu profili Lightroom.", "Analog Signature — migration of legacy Lightroom profile state."),
            "",
            L.t("s0192_removed_outdated_aikm_profiles") .. tostring(result.legacy_removed or 0),
            tr("Wyczyszczono cache profili Adobe: ", "Cleared Adobe profile cache: ") .. tostring(result.caches_deleted or 0),
            "",
            L.t("s0193_if_the_profile_browser_still_a"),
        }
        LrDialogs.message("Analog Signature", table.concat(lines, "\n"), "info")
    end
else
    logger.error("Plugin init DCP sync failed", {
        error = tostring(result),
    })
end

local presetsOk, presetsResult = pcall(function()
    return presetInstaller.installModularPresets(_PLUGIN.path, config)
end)

if presetsOk and presetsResult then
    logger.info("Plugin init modular preset sync", {
        target = tostring(presetsResult.target),
        installed = tostring(presetsResult.installed),
        skipped = tostring(presetsResult.skipped),
        missing = tostring(presetsResult.missing),
        errors = tostring(presetsResult.errors),
    })
else
    logger.error("Plugin init modular preset sync failed", {
        error = tostring(presetsResult),
    })
end
