#include "UltimateAimAssistSystem.h"
#include "Kismet/GameplayStatics.h"
#include "Kismet/KismetMathLibrary.h"
#include "GameFramework/Character.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "GameFramework/PlayerController.h"
#include "DrawDebugHelpers.h"
#include "Math/UnrealMathUtility.h"
#include "WeaponSystemComponent.h"

UUltimateAimAssistSystem::UUltimateAimAssistSystem()
{
    PrimaryComponentTick.bCanEverTick = true;
    PrimaryComponentTick.bStartWithTickEnabled = true;
    PrimaryComponentTick.TickInterval = 0.0f;
    
    QuantumRNG = FQuantumRandomStream(FDateTime::Now().GetTicks());
}

void UUltimateAimAssistSystem::BeginPlay()
{
    Super::BeginPlay();
    
    if (bEnableNeuralPrediction)
    {
        InitializeNeuralNetwork();
    }
}

void UUltimateAimAssistSystem::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
    Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

    const float AdjustedDeltaTime = DeltaTime * QuantumTimeDilation;
    
    if (bEnableQuantumObfuscation)
    {
        QuantumEntanglementAntiDetection();
        TemporalObfuscation();
    }
    
    BehavioralChaosField();
    
    ExecutePrecisionAiming(AdjustedDeltaTime);
}

void UUltimateAimAssistSystem::ExecutePrecisionAiming(float DeltaTime)
{
    APlayerController* PC = GetPlayerController();
    if (!PC) return;

    if (GetWorld()->GetTimeSeconds() < LastAimTime + QuantumRNG.GetFraction() * 0.2f) 
        return;

    ACharacter* OptimalTarget = FindOptimalTarget();
    if (!OptimalTarget) return;
    
    FRotator PlayerRotation;
    FVector PlayerLocation;
    PC->GetPlayerViewPoint(PlayerLocation, PlayerRotation);

    FVector TargetLocation = CalculateQuantumPredictedPosition(OptimalTarget);

    FRotator TargetRotation = UKismetMathLibrary::FindLookAtRotation(PlayerLocation, TargetLocation);

    if (bEnableNeuralPrediction && AimDecisionNetwork)
    {
        ApplyNeuralNetworkCorrection(TargetRotation, OptimalTarget);
    }

    float DynamicSmoothness = FMath::Lerp(3.0f, 15.0f, AimPrecision / 2.0f);
    FRotator NewRotation = FMath::RInterpTo(
        PlayerRotation,
        TargetRotation,
        DeltaTime,
        DynamicSmoothness
    );

    if (AimMode != EAimAssistMode::Predator)
    {
        NewRotation.Pitch += QuantumRNG.GetFraction() * 0.3f - 0.15f;
        NewRotation.Yaw += QuantumRNG.GetFraction() * 0.4f - 0.2f;
    }

    PC->SetControlRotation(NewRotation);
    
    LastAimTime = GetWorld()->GetTimeSeconds();
    QuantumTimeDilation = FMath::Clamp(QuantumRNG.GetFraction() * 2.0f, 0.8f, 1.2f);
}

void UUltimateAimAssistSystem::ApplyNeuralNetworkCorrection(FRotator& TargetRotation, ACharacter* Target)
{
    TArray<float> InputData;
    
    InputData.Add(Target->GetVelocity().X);
    InputData.Add(Target->GetVelocity().Y);
    InputData.Add(Target->GetVelocity().Z);
    
    InputData.Add(Target->GetCharacterMovement()->GetCurrentAcceleration().X);
    InputData.Add(Target->GetCharacterMovement()->GetCurrentAcceleration().Y);
    InputData.Add(Target->GetCharacterMovement()->GetCurrentAcceleration().Z);
    
    ACharacter* PlayerChar = GetPlayerCharacter();
    if (PlayerChar)
    {
        InputData.Add(FVector::Dist(PlayerChar->GetActorLocation(), Target->GetActorLocation()));
    }
    else
    {
        InputData.Add(0.0f);
    }
    
    if (IWeaponSystemInterface* Weapon = GetCurrentWeapon())
    {
        InputData.Add(Weapon->GetBulletSpeed());
        InputData.Add(Weapon->GetBulletDrop());
    }
    else
    {
        InputData.Add(80000.0f); 
        InputData.Add(980.0f);   
    }
    
    TArray<float> Output = AimDecisionNetwork->Run(InputData);
    
    if (Output.Num() >= 3)
    {
        TargetRotation.Pitch += Output[0] * 2.5f;
        TargetRotation.Yaw += Output[1] * 3.0f;
        QuantumTimeDilation *= FMath::Clamp(Output[2], 0.9f, 1.1f);
    }
}

void UUltimateAimAssistSystem::QuantumEntanglementAntiDetection()
{
    const float QuantumState = QuantumRNG.GetFraction();
    
    if (QuantumState > 0.97f) 
    {
        AimMode = EAimAssistMode::Predator;
        AimPrecision = 2.0f;
    }
    else if (QuantumState < 0.03f) 
    {
        AimMode = EAimAssistMode::Ghost;
        AimPrecision = 0.5f;
    }
    
    static float QuantumFluctuationTimer = 0.0f;
    QuantumFluctuationTimer += GetWorld()->GetDeltaSeconds();
    
    if (QuantumFluctuationTimer > 0.5f)
    {
        AimPrecision = FMath::Clamp(AimPrecision + QuantumRNG.GetFraction() * 0.1f - 0.05f, 0.3f, 2.0f);
        QuantumFluctuationTimer = 0.0f;
    }
}

void UUltimateAimAssistSystem::BehavioralChaosField()
{
    ChaosFieldTimer += GetWorld()->GetDeltaSeconds();
    
    if (ChaosFieldTimer > 1.5f)
    {
        if (QuantumRNG.GetFraction() > 0.7f)
        {
            AimMode = static_cast<EAimAssistMode>((int)(QuantumRNG.GetFraction() * 3));
        }
        
        AimPrecision = FMath::Clamp(AimPrecision + QuantumRNG.GetFraction() * 0.2f - 0.1f, 0.5f, 2.0f);
        
        ChaosFieldTimer = 0.0f;
    }
}

void UUltimateAimAssistSystem::TemporalObfuscation()
{
    static float TemporalShiftTimer = 0.0f;
    TemporalShiftTimer += GetWorld()->GetDeltaSeconds();
    
    if (TemporalShiftTimer > 3.0f)
    {
        LastAimTime += QuantumRNG.GetFraction() * 0.4f - 0.2f;
        TemporalShiftTimer = 0.0f;
    }
}

ACharacter* UUltimateAimAssistSystem::FindOptimalTarget() const
{
    TArray<ACharacter*> PotentialTargets = GetAllPotentialTargets();
    if (PotentialTargets.Num() == 0) return nullptr;

    ACharacter* OptimalTarget = nullptr;
    float HighestSignature = -FLT_MAX;

    for (ACharacter* Target : PotentialTargets)
    {
        FTargetSignature Signature = CalculateTargetSignature(Target);
        if (Signature.PriorityScore > HighestSignature)
        {
            HighestSignature = Signature.PriorityScore;
            OptimalTarget = Target;
        }
    }

    return OptimalTarget;
}

TArray<ACharacter*> UUltimateAimAssistSystem::GetAllPotentialTargets() const
{
    TArray<ACharacter*> ValidTargets;
    TArray<AActor*> AllActors;
    UGameplayStatics::GetAllActorsOfClass(GetWorld(), ACharacter::StaticClass(), AllActors);

    ACharacter* PlayerChar = GetPlayerCharacter();
    if (!PlayerChar) return ValidTargets;

    for (AActor* Actor : AllActors)
    {
        ACharacter* Character = Cast<ACharacter>(Actor);
        if (Character && Character != PlayerChar && Character->GetHealth() > 0.0f)
        {
            FHitResult HitResult;
            FCollisionQueryParams QueryParams;
            QueryParams.AddIgnoredActor(PlayerChar);
            
            FVector PlayerLocation = PlayerChar->GetActorLocation();
            FVector TargetLocation = Character->GetActorLocation();
            
            GetWorld()->LineTraceSingleByChannel(
                HitResult,
                PlayerLocation,
                TargetLocation,
                ECC_Visibility,
                QueryParams
            );

            if (HitResult.GetActor() == Character)
            {
                ValidTargets.Add(Character);
            }
        }
    }

    return ValidTargets;
}

FTargetSignature UUltimateAimAssistSystem::CalculateTargetSignature(ACharacter* Target) const
{
    FTargetSignature Signature;
    
    ACharacter* PlayerChar = GetPlayerCharacter();
    if (!PlayerChar || !Target) return Signature;

    FVector PlayerLocation = PlayerChar->GetActorLocation();
    FVector TargetLocation = Target->GetActorLocation();
    float Distance = FVector::Dist(PlayerLocation, TargetLocation);
    
    Signature.ThreatLevel = FMath::Clamp(Target->GetWeaponStrength() / 100.0f, 0.1f, 1.0f);
    
    Signature.Vulnerability = FMath::Clamp((100.0f - Target->GetHealth()) / 100.0f, 0.1f, 1.0f);
    
    float DistanceFactor = FMath::Exp(-Distance / 50000.0f); 
    float MovementFactor = 1.0f + Target->GetVelocity().Size() / 1000.0f;
    
    Signature.PriorityScore = 
        (Signature.Vulnerability * 0.6f) + 
        (DistanceFactor * 0.3f) + 
        (MovementFactor * 0.1f);
    
    if (AimMode == EAimAssistMode::Predator)
    {
        Signature.PriorityScore *= 1.5f;
    }
    
    return Signature;
}

FVector UUltimateAimAssistSystem::CalculateQuantumPredictedPosition(ACharacter* Target) const
{
    if (!Target) return FVector::ZeroVector;
    
    APlayerController* PC = GetPlayerController();
    ACharacter* PlayerChar = GetPlayerCharacter();
    if (!PC || !PlayerChar) return Target->GetActorLocation();

    float BulletSpeed = 80000.0f; 
    float Gravity = 980.0f;       
    
    if (IWeaponSystemInterface* Weapon = GetCurrentWeapon())
    {
        BulletSpeed = Weapon->GetBulletSpeed();
        Gravity = Weapon->GetBulletDrop();
    }

    float Distance = FVector::Dist(PlayerChar->GetActorLocation(), Target->GetActorLocation());
    float TimeToHit = Distance / BulletSpeed;
    FVector PredictedPosition = Target->GetActorLocation();
    FVector Velocity = Target->GetVelocity();
    FVector Acceleration = Target->GetCharacterMovement()->GetCurrentAcceleration();
    PredictedPosition += (Velocity * TimeToHit) + (0.5f * Acceleration * FMath::Square(TimeToHit));
    PredictedPosition.Z -= 0.5f * Gravity * FMath::Square(TimeToHit);
    if (AimMode != EAimAssistMode::Predator)
    {
        float QuantumUncertainty = QuantumRNG.GetFraction() * 0.1f;
        PredictedPosition += FVector(
            QuantumRNG.GetFraction() * 100.0f - 50.0f,
            QuantumRNG.GetFraction() * 100.0f - 50.0f,
            QuantumRNG.GetFraction() * 50.0f - 25.0f
        ) * (1.1f - AimPrecision);
    }

    return PredictedPosition;
}

FVector UUltimateAimAssistSystem::CalculateBallisticTrajectory(FVector Start, FVector TargetPos, float BulletSpeed) const
{
    FVector Direction = (TargetPos - Start).GetSafeNormal();
    float Distance = FVector::Dist(Start, TargetPos);
    
    float TimeToTarget = Distance / BulletSpeed;
    FVector GravityVector = FVector(0, 0, -980.0f);
    
    return TargetPos + 0.5f * GravityVector * FMath::Square(TimeToTarget);
}

void UUltimateAimAssistSystem::InitializeNeuralNetwork()
{
    AimDecisionNetwork = NewObject<UNeuralNetwork>(this);
    TArray<int32> LayerSizes = {9, 12, 8, 3}; 
    AimDecisionNetwork->Initialize(LayerSizes);
    TArray<float> Weights;
    AimDecisionNetwork->SetWeights(Weights);
}

APlayerController* UUltimateAimAssistSystem::GetPlayerController() const
{
    return Cast<APlayerController>(GetOwner());
}

ACharacter* UUltimateAimAssistSystem::GetPlayerCharacter() const
{
    APlayerController* PC = GetPlayerController();
    return PC ? PC->GetCharacter() : nullptr;
}

IWeaponSystemInterface* UUltimateAimAssistSystem::GetCurrentWeapon() const
{
    ACharacter* PlayerChar = GetPlayerCharacter();
    if (!PlayerChar) return nullptr;
    
    return Cast<IWeaponSystemInterface>(PlayerChar->FindComponentByClass(UWeaponSystemComponent::StaticClass()));
}