import Foundation

public enum EditorRenderReason: String, Hashable, Codable, Sendable {
    case initial
    case sourceChange
    case activeLineChange
    case viewUpdate
}

public struct EditorRenderPassMetric: Hashable, Sendable {
    public var reason: EditorRenderReason
    public var durationMilliseconds: Double
    public var characterCount: Int
    public var lineCount: Int
    public var dirtyLineCount: Int
    public var activeLineIndex: Int
    public var isFullRender: Bool
    public var restoredScrollPosition: Bool

    public init(
        reason: EditorRenderReason,
        durationMilliseconds: Double,
        characterCount: Int,
        lineCount: Int,
        dirtyLineCount: Int = 0,
        activeLineIndex: Int,
        isFullRender: Bool = false,
        restoredScrollPosition: Bool = false
    ) {
        self.reason = reason
        self.durationMilliseconds = durationMilliseconds
        self.characterCount = characterCount
        self.lineCount = lineCount
        self.dirtyLineCount = dirtyLineCount
        self.activeLineIndex = activeLineIndex
        self.isFullRender = isFullRender
        self.restoredScrollPosition = restoredScrollPosition
    }
}

public struct EditorInstrumentationSnapshot: Hashable, Sendable {
    public var sourceChangeCount: Int
    public var selectionChangeCount: Int
    public var selectionUpdateCount: Int
    public var activeLineChangeCount: Int
    public var renderPassCount: Int
    public var fullRenderCount: Int
    public var totalDirtyLineCount: Int
    public var scrollRestorationCount: Int
    public var totalRenderDurationMilliseconds: Double
    public var lastRenderDurationMilliseconds: Double
    public var lastRenderCharacterCount: Int
    public var lastRenderLineCount: Int
    public var lastDirtyLineCount: Int
    public var lastRenderReason: EditorRenderReason?

    public init(
        sourceChangeCount: Int = 0,
        selectionChangeCount: Int = 0,
        selectionUpdateCount: Int = 0,
        activeLineChangeCount: Int = 0,
        renderPassCount: Int = 0,
        fullRenderCount: Int = 0,
        totalDirtyLineCount: Int = 0,
        scrollRestorationCount: Int = 0,
        totalRenderDurationMilliseconds: Double = 0,
        lastRenderDurationMilliseconds: Double = 0,
        lastRenderCharacterCount: Int = 0,
        lastRenderLineCount: Int = 0,
        lastDirtyLineCount: Int = 0,
        lastRenderReason: EditorRenderReason? = nil
    ) {
        self.sourceChangeCount = sourceChangeCount
        self.selectionChangeCount = selectionChangeCount
        self.selectionUpdateCount = selectionUpdateCount
        self.activeLineChangeCount = activeLineChangeCount
        self.renderPassCount = renderPassCount
        self.fullRenderCount = fullRenderCount
        self.totalDirtyLineCount = totalDirtyLineCount
        self.scrollRestorationCount = scrollRestorationCount
        self.totalRenderDurationMilliseconds = totalRenderDurationMilliseconds
        self.lastRenderDurationMilliseconds = lastRenderDurationMilliseconds
        self.lastRenderCharacterCount = lastRenderCharacterCount
        self.lastRenderLineCount = lastRenderLineCount
        self.lastDirtyLineCount = lastDirtyLineCount
        self.lastRenderReason = lastRenderReason
    }

    public var averageRenderDurationMilliseconds: Double {
        guard renderPassCount > 0 else { return 0 }
        return totalRenderDurationMilliseconds / Double(renderPassCount)
    }

    public mutating func recordSourceChange() {
        sourceChangeCount += 1
    }

    public mutating func recordSelectionChange() {
        selectionChangeCount += 1
        selectionUpdateCount += 1
    }

    public mutating func recordActiveLineChange() {
        activeLineChangeCount += 1
    }

    public mutating func recordRenderPass(_ metric: EditorRenderPassMetric) {
        renderPassCount += 1
        if metric.isFullRender {
            fullRenderCount += 1
        }
        totalDirtyLineCount += metric.dirtyLineCount
        if metric.restoredScrollPosition {
            scrollRestorationCount += 1
        }
        totalRenderDurationMilliseconds += metric.durationMilliseconds
        lastRenderDurationMilliseconds = metric.durationMilliseconds
        lastRenderCharacterCount = metric.characterCount
        lastRenderLineCount = metric.lineCount
        lastDirtyLineCount = metric.dirtyLineCount
        lastRenderReason = metric.reason
    }
}

#if DEBUG
public enum EditorDiagnostics {
    public static var isRenderLoggingEnabled = false

    public static func logRenderPass(_ metric: EditorRenderPassMetric) {
        guard isRenderLoggingEnabled else { return }

        print(
            "SaplingEditor render reason=\(metric.reason.rawValue) "
                + "full=\(metric.isFullRender) "
                + "dirtyLines=\(metric.dirtyLineCount)/\(metric.lineCount) "
                + "chars=\(metric.characterCount) "
                + "activeLine=\(metric.activeLineIndex) "
                + "durationMs=\(String(format: "%.3f", metric.durationMilliseconds)) "
                + "scrollRestored=\(metric.restoredScrollPosition)"
        )
    }
}
#endif
