local RunService = game:GetService("RunService")
local Players = game:GetService("Players")
local TweenService = game:GetService("TweenService")
local VirtualInputManager = game:GetService("VirtualInputManager")

local Player = Players.LocalPlayer
if not Player then error("LocalPlayer tidak ditemukan!") end

-- Cache fungsi matematika untuk performa
local min = math.min
local max = math.max
local abs = math.abs
local deg = math.deg
local acos = math.acos
local sqrt = math.sqrt
local clamp = math.clamp

-- Flag debug (set false jika tidak ingin banyak log)
local DEBUG = true
local function AdvancedLog(level, message, data)
    if not DEBUG then return end
    local timestamp = os.date("%Y-%m-%d %H:%M:%S")
    print(string.format("[%s] [%s]: %s", timestamp, level, message))
    if data then
        for k, v in pairs(data) do
            print(string.format("  %s: %s", k, tostring(v)))
        end
    end
end

-- Konstanta State
local STATE_IDLE         = "Idle"
local STATE_TRACKING     = "Tracking"
local STATE_ANTICIPATING = "Anticipating"
local STATE_PARIED       = "Parried"
local STATE_COOLDOWN     = "Cooldown"
local STATE_SPAM         = "Spam"

local MAX_IMPACT_TIME = 4.0            -- Maksimum waktu impact (detik)
local INCOMING_ANGLE_THRESHOLD = 20    -- Threshold sudut (derajat)

---------------------------------
-- Advanced Parry Controller (State Machine)
---------------------------------
local AdvancedParryController = {}
AdvancedParryController.__index = AdvancedParryController

function AdvancedParryController.new()
    local self = setmetatable({}, AdvancedParryController)
    self.Player = Player
    self.State = STATE_IDLE
    self.LastParryTime = tick()
    self.CooldownTime = 0.75          -- Cooldown normal (detik)
    self.BaseThreshold = 0.45         -- Threshold dasar (detik)
    self.BallStopped = false
    self.PreviousVelocity = nil
    self.SmoothedAccel = Vector3.new(0, 0, 0)
    self.CurrentBall = nil
    self.Connection = nil
    self.LastSpamTime = 0
    self.BaseSpamInterval = 0.1

    -- GUI Full Screen Popup
    self.FullScreenGui = nil
    self.AdditionalInfoLabel = nil

    -- Overpower Mode: parry akan menjadi jauh lebih agresif dan responsif
    self.OverpowerMode = true
    self.OverpowerThresholdMultiplier = 0.5  -- Mengurangi threshold agar trigger lebih cepat
    self.OverpowerCooldown = 0.1             -- Cooldown sangat rendah pada mode ini

    return self
end

-- Dapatkan ping lokal (detik)
function AdvancedParryController:GetLocalPing()
    local success, ping = pcall(function() return self.Player:GetNetworkPing() end)
    if not success then
        AdvancedLog("WARN", "Gagal mendapatkan ping, default 0.1 detik.", nil)
        return 0.1
    end
    return ping or 0.1
end

-- Hitung threshold dinamis berdasarkan ping (dikurangi jika OverpowerMode aktif)
function AdvancedParryController:ComputeDynamicThreshold()
    local adjustmentFactor = 0.5
    local threshold = self.BaseThreshold + (self:GetLocalPing() * adjustmentFactor)
    if self.OverpowerMode then
        threshold = threshold * self.OverpowerThresholdMultiplier
    end
    return threshold
end

-- Seleksi bola target terbaik (paling mengancam) dari folder "Balls"
function AdvancedParryController:SelectTargetBall()
    local ballsFolder = workspace:FindFirstChild("Balls")
    local bestBall = nil
    local bestTImpact = math.huge
    if ballsFolder then
        local character = self.Player.Character
        local HRP = character and character:FindFirstChild("HumanoidRootPart")
        if not HRP then return nil, math.huge end
        for _, ball in ipairs(ballsFolder:GetChildren()) do
            if ball:GetAttribute("realBall") and ball:GetAttribute("target") == self.Player.Name then
                local distance = (HRP.Position - ball.Position).Magnitude
                local ballVelocity
                local success = pcall(function() ballVelocity = ball.zoomies.VectorVelocity end)
                if success and typeof(ballVelocity) == "Vector3" then
                    -- Untuk seleksi, asumsikan percepatan nol
                    local tImpact = self:PredictImpactTime(distance, ballVelocity, Vector3.new(0,0,0))
                    if tImpact < bestTImpact then
                        bestTImpact = tImpact
                        bestBall = ball
                    end
                end
            end
        end
    end
    return bestBall, bestTImpact
end

-- Reset koneksi event bila diperlukan
function AdvancedParryController:ResetConnection()
    if self.Connection then
        self.Connection:Disconnect()
        self.Connection = nil
    end
end

-- Inisialisasi listener bola untuk mendeteksi perubahan atribut target
function AdvancedParryController:InitializeBallListener()
    self:ResetConnection()
    local ball = self:SelectTargetBall() -- Menggunakan target terbaik
    if ball then
        self.CurrentBall = ball
        self.Connection = ball:GetAttributeChangedSignal("target"):Connect(function()
            self.State = STATE_TRACKING
            AdvancedLog("INFO", "Atribut target bola berubah, reset state ke TRACKING.", {Ball = ball.Name})
        end)
        AdvancedLog("INFO", "Listener bola diinisialisasi.", {Ball = ball.Name})
    else
        self.CurrentBall = nil
        AdvancedLog("WARN", "Bola tidak ditemukan. Listener tidak diinisialisasi.", nil)
    end
end

-- Inisialisasi GUI full-screen (popup hitam) dengan animasi smooth.
-- Tampilan: layar penuh yang sedikit transparan, di tengah ada tulisan "Status : Auto Parry",
-- label tambahan untuk informasi penting (misal: Ping & State) dan tombol Exit.
function AdvancedParryController:InitializeGUI()
    local playerGui = self.Player:WaitForChild("PlayerGui")
    local screenGui = Instance.new("ScreenGui")
    screenGui.Name = "FullScreenPopupGui"
    screenGui.ResetOnSpawn = false
    screenGui.IgnoreGuiInset = true  -- Menutupi seluruh layar (termasuk area inset)
    screenGui.Parent = playerGui

    -- Frame full screen dengan background hitam (mulai dari transparansi penuh untuk animasi)
    local fullScreenFrame = Instance.new("Frame")
    fullScreenFrame.Name = "FullScreenFrame"
    fullScreenFrame.BackgroundColor3 = Color3.new(0, 0, 0)
    fullScreenFrame.BackgroundTransparency = 1  -- Mulai transparan, akan tween ke 0.2
    fullScreenFrame.Size = UDim2.new(1, 0, 1, 0)
    fullScreenFrame.Position = UDim2.new(0, 0, 0, 0)
    fullScreenFrame.Parent = screenGui

    -- Label utama untuk status (di tengah layar)
    local statusLabel = Instance.new("TextLabel")
    statusLabel.Name = "StatusLabel"
    statusLabel.BackgroundTransparency = 1
    statusLabel.Text = "Just - Hub : Auto Parry"
    statusLabel.TextColor3 = Color3.new(1, 1, 1)
    statusLabel.Font = Enum.Font.SourceSansBold
    statusLabel.TextSize = 36
    statusLabel.AnchorPoint = Vector2.new(0.5, 0.5)
    statusLabel.Position = UDim2.new(0.5, 0, 0.35, 0)
    -- Mulai dengan scale 0 (ukuran 0) untuk animasi scale-in
    statusLabel.Size = UDim2.new(0, 0, 0, 0)
    statusLabel.Parent = fullScreenFrame

    -- Label tambahan untuk informasi penting (misal: ping dan state)
    local additionalInfoLabel = Instance.new("TextLabel")
    additionalInfoLabel.Name = "AdditionalInfoLabel"
    additionalInfoLabel.BackgroundTransparency = 1
    additionalInfoLabel.Text = "Loading info..."
    additionalInfoLabel.TextColor3 = Color3.new(1, 1, 1)
    additionalInfoLabel.Font = Enum.Font.SourceSans
    additionalInfoLabel.TextSize = 24
    additionalInfoLabel.AnchorPoint = Vector2.new(0.5, 0.5)
    additionalInfoLabel.Position = UDim2.new(0.5, 0, 0.5, 0)
    additionalInfoLabel.Size = UDim2.new(0, 400, 0, 50)
    additionalInfoLabel.Parent = fullScreenFrame
    self.AdditionalInfoLabel = additionalInfoLabel

    -- Tombol Exit di bawah label tambahan
    local exitButton = Instance.new("TextButton")
    exitButton.Name = "ExitButton"
    exitButton.Text = "Exit"
    exitButton.TextColor3 = Color3.new(1, 1, 1)
    exitButton.Font = Enum.Font.SourceSansBold
    exitButton.TextSize = 28
    exitButton.BackgroundColor3 = Color3.new(0.3, 0.3, 0.3)
    exitButton.Size = UDim2.new(0, 150, 0, 50)
    exitButton.AnchorPoint = Vector2.new(0.5, 0)
    exitButton.Position = UDim2.new(0.5, 0, 0.65, 0)
    exitButton.Parent = fullScreenFrame

    exitButton.MouseButton1Click:Connect(function()
        Player:Kick("Anda telah keluar dari game.")
    end)

    self.FullScreenGui = screenGui

    -- Tween untuk animasi fade-in (background transparency dari 1 ke 0.2)
    local tweenInfo = TweenInfo.new(1, Enum.EasingStyle.Sine, Enum.EasingDirection.Out)
    local tweenFrame = TweenService:Create(fullScreenFrame, tweenInfo, {BackgroundTransparency = 0.2})
    tweenFrame:Play()

    -- Tween untuk animasi scale-in label utama (dari 0 ke ukuran asli)
    local tweenLabel = TweenService:Create(statusLabel, tweenInfo, {Size = UDim2.new(0, 400, 0, 100)})
    tweenLabel:Play()

    -- Tween untuk animasi scale-in label informasi tambahan (sedikit scale-up)
    local tweenInfoLabel = TweenService:Create(additionalInfoLabel, tweenInfo, {TextTransparency = 0})
    tweenInfoLabel:Play()
end

-- Fungsi untuk mengirim event parry (klik mouse) dengan delay minimal
function AdvancedParryController:AttemptParry()
    VirtualInputManager:SendMouseButtonEvent(0, 0, 0, true, game, 0)
    task.delay(0.01, function()
        VirtualInputManager:SendMouseButtonEvent(0, 0, 0, false, game, 0)
    end)
    self.LastParryTime = tick()
end

-- Lakukan exponential smoothing pada percepatan (lebih agresif pada mode overpowered)
function AdvancedParryController:SmoothAcceleration(currentVelocity, dt)
    local alpha = self.OverpowerMode and 0.8 or 0.4
    if not self.PreviousVelocity then
        self.PreviousVelocity = currentVelocity
        return Vector3.new(0, 0, 0)
    end
    local instantAccel = (currentVelocity - self.PreviousVelocity) / dt
    self.SmoothedAccel = self.SmoothedAccel:Lerp(instantAccel, alpha)
    self.PreviousVelocity = currentVelocity
    return self.SmoothedAccel
end

-- Prediksi waktu impact menggunakan rumus: s = vt + ½at²
function AdvancedParryController:PredictImpactTime(distance, currentVelocity, accel)
    local v = currentVelocity.Magnitude
    local a = accel.Magnitude
    if abs(a) < 0.001 then
        return (v > 0) and (distance / v) or math.huge
    end
    local discriminant = v * v + 2 * a * distance
    if discriminant < 0 then return math.huge end
    local t1 = (-v + sqrt(discriminant)) / a
    local t2 = (-v - sqrt(discriminant)) / a
    local t = math.huge
    if t1 > 0 then t = t1 end
    if t2 > 0 and t2 < t then t = t2 end
    return t
end

-- Prediksi posisi bola saat impact (asumsi percepatan konstan)
function AdvancedParryController:PredictImpactPosition(ballPos, currentVelocity, accel, t)
    return ballPos + currentVelocity * t + 0.5 * accel * t * t
end

-- Hitung sudut incoming: antara arah kecepatan bola dan vektor dari bola ke HRP
function AdvancedParryController:ComputeIncomingAngle(ballVelocity, HRP, ballPos)
    local mag = ballVelocity.Magnitude
    if mag <= 0 then return 180 end
    local incomingVec = (HRP.Position - ballPos).Unit
    local velDir = ballVelocity.Unit
    local dot = clamp(velDir:Dot(incomingVec), -1, 1)
    return deg(acos(dot))
end

-- Update state berdasarkan data bola, karakter, prediksi, dan validasi ekstra
function AdvancedParryController:UpdateState(dt)
    local now = tick()
    local ball, tImpactCandidate = self:SelectTargetBall()
    if not ball then
        self.State = STATE_IDLE
        AdvancedLog("DEBUG", "Tidak ada bola target ditemukan.", nil)
        self:InitializeBallListener()
        return
    end
    self.CurrentBall = ball

    local character = self.Player.Character
    local HRP = character and character:FindFirstChild("HumanoidRootPart")
    if not HRP then
        self.State = STATE_IDLE
        AdvancedLog("WARN", "HumanoidRootPart tidak ditemukan.", nil)
        return
    end

    local success, ballVelocity = pcall(function() return ball.zoomies.VectorVelocity end)
    if not success or typeof(ballVelocity) ~= "Vector3" then
        AdvancedLog("ERROR", "VectorVelocity bola tidak valid.", {Error = tostring(ballVelocity)})
        return
    end

    local speed = ballVelocity.Magnitude
    local dynamicThreshold = self:ComputeDynamicThreshold()
    local threshold = min(dynamicThreshold, MAX_IMPACT_TIME)
    local distance = (HRP.Position - ball.Position).Magnitude

    -- Hitung percepatan, waktu impact, dan incoming angle
    local prevVelocity = self.PreviousVelocity or ballVelocity
    local accel = self:SmoothAcceleration(ballVelocity, dt)
    local tImpact = self:PredictImpactTime(distance, ballVelocity, accel)
    local incomingAngle = self:ComputeIncomingAngle(ballVelocity, HRP, ball.Position)

    -- Validasi target bola
    local targetAttr = ball:GetAttribute("target")
    if type(targetAttr) ~= "string" or targetAttr ~= self.Player.Name then
        self.State = STATE_IDLE
        AdvancedLog("DEBUG", "Bola tidak mengincar local player.", {Target = tostring(targetAttr)})
        return
    end

    -- MODE OVERPOWER: Jika tImpact sudah sangat rendah, trigger parry langsung (bypass kondisi normal)
    if self.OverpowerMode and tImpact <= threshold * 1.5 then
        self:AttemptParry()
        self.State = STATE_PARIED
        AdvancedLog("ACTION", "OVERPOWER: Forced parry triggered.", {tImpact = tImpact, IncomingAngle = incomingAngle})
    end

    -- Jika bola berada dalam rentang 10-20 studs dengan incoming angle rendah
    if distance >= 10 and distance <= 20 and incomingAngle < INCOMING_ANGLE_THRESHOLD then
        self:AttemptParry()
        self.State = STATE_PARIED
        AdvancedLog("ACTION", "Direct parry triggered.", {Distance = distance, Angle = incomingAngle, tImpact = tImpact})
    end

    -- Mode SPAM: Jika bola sangat dekat (<= 10 studs)
    if distance <= 10 then
        if self.State ~= STATE_SPAM then
            self.State = STATE_SPAM
            AdvancedLog("INFO", "Entering SPAM mode.", {Distance = distance})
        end
        local spamInterval = max(self.BaseSpamInterval * (1 - (speed / 100)), 0.03)
        if now - self.LastSpamTime >= spamInterval then
            self:AttemptParry()
            self.LastSpamTime = now
            AdvancedLog("ACTION", "Spam parry triggered.", {Distance = distance, SpamInterval = spamInterval})
        end
        return
    elseif self.State == STATE_SPAM then
        self.State = STATE_TRACKING
        AdvancedLog("INFO", "Exiting SPAM mode.", {Distance = distance})
    end

    -- Jika kecepatan bola sangat rendah, anggap bola berhenti
    if speed < 0.1 then
        if not self.BallStopped then
            self.BallStopped = true
            self.State = STATE_IDLE
            AdvancedLog("INFO", "Ball stopped.", {Speed = speed})
        end
        return
    elseif self.BallStopped then
        self.BallStopped = false
        if self.State == STATE_IDLE then
            self.State = STATE_TRACKING
            AdvancedLog("INFO", "Ball resumed.", {Speed = speed})
        end
    end

    -- Hitung curve angle (perubahan arah) antara frame sebelumnya dan saat ini
    local curveAngle = 0
    if prevVelocity.Magnitude > 0 and ballVelocity.Magnitude > 0 then
        local dot = clamp(prevVelocity.Unit:Dot(ballVelocity.Unit), -1, 1)
        curveAngle = deg(acos(dot))
    end

    AdvancedLog("DEBUG", "Ball metrics updated.", {
        Speed = speed,
        CurveAngle = curveAngle,
        Distance = distance,
        tImpact = tImpact,
        DynamicThreshold = dynamicThreshold,
        IncomingAngle = incomingAngle,
        State = self.State
    })

    if tImpact < dynamicThreshold * 0.5 and self.State ~= STATE_ANTICIPATING then
        self.State = STATE_ANTICIPATING
        AdvancedLog("INFO", "Entering ANTICIPATING.", {tImpact = tImpact})
    end

    if (self.State == STATE_TRACKING or self.State == STATE_ANTICIPATING) then
        if tImpact <= threshold or (curveAngle >= 15 and tImpact <= MAX_IMPACT_TIME) then
            self:AttemptParry()
            self.State = STATE_PARIED
            AdvancedLog("ACTION", "Parry triggered.", {tImpact = tImpact, CurveAngle = curveAngle})
        end
    end

    if self.State == STATE_PARIED then
        local effectiveCooldown = self.OverpowerMode and self.OverpowerCooldown or self.CooldownTime
        if now - self.LastParryTime >= (threshold + effectiveCooldown) then
            self.State = STATE_COOLDOWN
            AdvancedLog("INFO", "Entering COOLDOWN.", {Elapsed = now - self.LastParryTime})
        end
    end

    if self.State == STATE_COOLDOWN then
        local effectiveCooldownFinish = self.OverpowerMode and (self.OverpowerCooldown + 0.05) or (self.CooldownTime + 0.5)
        if now - self.LastParryTime >= (threshold + effectiveCooldownFinish) then
            self.State = STATE_TRACKING
            AdvancedLog("INFO", "Cooldown finished, resuming TRACKING.", nil)
        end
    end

    if curveAngle > 45 and tImpact < threshold then
        self.State = STATE_TRACKING
        AdvancedLog("INFO", "Reset parry due to sharp curve.", {CurveAngle = curveAngle})
    end

    if distance <= 0 then
        AdvancedLog("WARN", "Invalid distance.", {Distance = distance})
    end

    -- Update label tambahan dengan informasi penting (misal: ping & state)
    if self.AdditionalInfoLabel then
        local pingMS = math.floor(self:GetLocalPing() * 1000)
        self.AdditionalInfoLabel.Text = string.format("Ping: %d ms  |  State: %s", pingMS, self.State)
    end
end

-------------------------------
-- Main Update Loop
-------------------------------
local controller = AdvancedParryController.new()
controller:InitializeBallListener()
controller:InitializeGUI()

RunService.Heartbeat:Connect(function(dt)
    pcall(function()
        controller:UpdateState(dt)
    end)
end)