AddonDiagnostics = AddonDiagnostics or {}
AddonDiagnostics.UniversalPerformanceEvidence = {}
local UniversalPerformanceEvidence = AddonDiagnostics.UniversalPerformanceEvidence

local MAX_MARKER_PERFORMANCE_SNAPSHOTS = 25
local MAX_MARKER_SNAPSHOT_CHARACTERS = 240
local MAX_TIMELINE_LINES = 640
local startProfilerSummary = nil
local stopProfilerSummary = nil
local markerPerformanceSnapshots = {}
local markerPerformanceSnapshotCount = 0
local originalSessionStart = nil
local originalSessionStop = nil
local originalMarkProblemNow = nil
local originalTimelineEvidence = nil

local function FormatProfilerPercent(value)
    local percent = tonumber(value) or 0
    if percent >= 1 then
        return string.format("%.0f%%", percent)
    end
    if percent >= 0.1 then
        return string.format("%.1f%%", percent)
    end
    if percent >= 0.01 then
        return string.format("%.2f%%", percent)
    end
    return "0%"
end

local function GetOverallAddonMetricPercent(metric)
    if not C_AddOnProfiler then
        return nil
    end
    if type(C_AddOnProfiler.IsEnabled) ~= "function" then
        return nil
    end
    if type(C_AddOnProfiler.GetApplicationMetric) ~= "function" then
        return nil
    end
    if type(C_AddOnProfiler.GetOverallMetric) ~= "function" then
        return nil
    end

    local okayEnabled, enabled = pcall(C_AddOnProfiler.IsEnabled)
    if not okayEnabled or enabled ~= true then
        return nil
    end

    local okayApplication, applicationValue = pcall(C_AddOnProfiler.GetApplicationMetric, metric)
    local okayOverall, overallValue = pcall(C_AddOnProfiler.GetOverallMetric, metric)
    if not okayApplication or not okayOverall then
        return nil
    end

    local application = tonumber(applicationValue) or 0
    local overall = tonumber(overallValue) or 0
    if application <= 0 then
        return 0
    end

    local percent = (overall / application) * 100
    if percent < 0 then
        return 0
    end
    return percent
end

local function CaptureProfilerSummary()
    if not Enum or not Enum.AddOnProfilerMetric then
        return "Overall addon CPU C unavailable | A unavailable | P unavailable"
    end

    local current = GetOverallAddonMetricPercent(Enum.AddOnProfilerMetric.RecentAverageTime)
    local average = GetOverallAddonMetricPercent(Enum.AddOnProfilerMetric.SessionAverageTime)
    local peak = GetOverallAddonMetricPercent(Enum.AddOnProfilerMetric.PeakTime)
    if current == nil or average == nil or peak == nil then
        return "Overall addon CPU C unavailable | A unavailable | P unavailable"
    end

    return string.format(
        "Overall addon CPU C %s | A %s | P %s",
        FormatProfilerPercent(current),
        FormatProfilerPercent(average),
        FormatProfilerPercent(peak)
    )
end

local function CaptureMarkerPerformanceSnapshot()
    local fpsText = "unavailable"
    if type(GetFramerate) == "function" then
        local fps = tonumber(GetFramerate())
        if fps then
            fpsText = string.format("%.1f", math.max(0, fps))
        end
    end

    local homeText = "unavailable"
    local worldText = "unavailable"
    if type(GetNetStats) == "function" then
        local _, _, latencyHome, latencyWorld = GetNetStats()
        local home = tonumber(latencyHome)
        local world = tonumber(latencyWorld)
        if home then
            homeText = string.format("%.0f", math.max(0, home))
        end
        if world then
            worldText = string.format("%.0f", math.max(0, world))
        end
    end

    local memoryText = "unavailable"
    if type(collectgarbage) == "function" then
        local memoryOkay, rawMemory = pcall(collectgarbage, "count")
        if memoryOkay then
            local memory = tonumber(rawMemory)
            if memory then
                memoryText = string.format("%.1f", math.max(0, memory))
            end
        end
    end

    local snapshot = string.format(
        "Marker: FPS %s | H %s ms | W %s ms | Lua %s KB | %s",
        fpsText,
        homeText,
        worldText,
        memoryText,
        CaptureProfilerSummary()
    )
    if string.len(snapshot) > MAX_MARKER_SNAPSHOT_CHARACTERS then
        return string.sub(snapshot, 1, MAX_MARKER_SNAPSHOT_CHARACTERS)
    end
    return snapshot
end

local function ResetPerformanceEvidence()
    startProfilerSummary = nil
    stopProfilerSummary = nil
    markerPerformanceSnapshots = {}
    markerPerformanceSnapshotCount = 0
end

local function AppendTimelineLine(output, line)
    if output == "" then
        return line
    end
    return string.format("%s\n%s", output, line)
end

local function BuildAugmentedTimelineEvidence(evidence)
    if type(evidence) ~= "string" or evidence == "" then
        return evidence
    end

    local output = ""
    local startIndex = 1
    local markerIndex = 0

    for index = 1, MAX_TIMELINE_LINES do
        local endIndex = string.find(evidence, "\n", startIndex, true)
        local line = nil
        if endIndex then
            line = string.sub(evidence, startIndex, endIndex - 1)
        else
            line = string.sub(evidence, startIndex)
        end

        local augmentedLine = line
        if startProfilerSummary and string.find(line, " | PERFORMANCE | Start:", 1, true) then
            augmentedLine = string.format("%s | %s", line, startProfilerSummary)
        elseif stopProfilerSummary and string.find(line, " | PERFORMANCE | Stop:", 1, true) then
            augmentedLine = string.format("%s | %s", line, stopProfilerSummary)
        elseif string.find(line, " | MARKER | Problem marked", 1, true) then
            markerIndex = markerIndex + 1
            local snapshot = rawget(markerPerformanceSnapshots, markerIndex)
            if type(snapshot) == "string" and snapshot ~= "" then
                augmentedLine = string.format("%s | %s", line, snapshot)
            end
        end

        output = AppendTimelineLine(output, augmentedLine)
        if not endIndex then
            return output
        end
        startIndex = endIndex + 1
    end

    return output
end

local function WrappedSessionStart(self)
    local started = originalSessionStart(self)
    if started then
        ResetPerformanceEvidence()
        startProfilerSummary = CaptureProfilerSummary()
    end
    return started
end

local function WrappedMarkProblemNow(self)
    local marked, elapsed = originalMarkProblemNow(self)
    if marked and markerPerformanceSnapshotCount < MAX_MARKER_PERFORMANCE_SNAPSHOTS then
        markerPerformanceSnapshotCount = markerPerformanceSnapshotCount + 1
        markerPerformanceSnapshots[markerPerformanceSnapshotCount] = CaptureMarkerPerformanceSnapshot()
    end
    return marked, elapsed
end

local function WrappedSessionStop(self)
    local stopped, elapsed = originalSessionStop(self)
    if stopped then
        stopProfilerSummary = CaptureProfilerSummary()
    end
    return stopped, elapsed
end

local function WrappedTimelineEvidence(self)
    return BuildAugmentedTimelineEvidence(originalTimelineEvidence(self))
end

function UniversalPerformanceEvidence:GetMarkerPerformanceSnapshotCount()
    return markerPerformanceSnapshotCount
end

local function AttachPerformanceEvidenceLifecycle()
    local controller = AddonDiagnostics.SessionController
    local timeline = AddonDiagnostics.SessionTimeline
    if not controller or not timeline then
        return
    end
    if type(controller.Start) ~= "function" or type(controller.Stop) ~= "function" then
        return
    end
    if type(controller.MarkProblemNow) ~= "function" or type(timeline.GetTimelineEvidence) ~= "function" then
        return
    end
    if originalSessionStart ~= nil or originalSessionStop ~= nil then
        return
    end

    originalSessionStart = controller.Start
    originalSessionStop = controller.Stop
    originalMarkProblemNow = controller.MarkProblemNow
    originalTimelineEvidence = timeline.GetTimelineEvidence
    controller.Start = WrappedSessionStart
    controller.Stop = WrappedSessionStop
    controller.MarkProblemNow = WrappedMarkProblemNow
    timeline.GetTimelineEvidence = WrappedTimelineEvidence
end

AttachPerformanceEvidenceLifecycle()
