#include "PS_Editor_Pawn.h"
#include "Camera/CameraComponent.h"
#include "GameFramework/SpringArmComponent.h"
#include "EnhancedInputComponent.h"
#include "EnhancedInputSubsystems.h"
#include "InputAction.h"
#include "InputMappingContext.h"
#include "InputModifiers.h"
#include "InputTriggers.h"
#include "GameFramework/PlayerController.h"

APS_Editor_Pawn::APS_Editor_Pawn()
{
	PrimaryActorTick.bCanEverTick = true;

	RootScene = CreateDefaultSubobject<USceneComponent>(TEXT("RootScene"));
	SetRootComponent(RootScene);

	SpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("SpringArm"));
	SpringArm->SetupAttachment(RootScene);
	SpringArm->TargetArmLength = 0.0f;
	SpringArm->bDoCollisionTest = false;
	SpringArm->bEnableCameraLag = false;
	SpringArm->bUsePawnControlRotation = false;

	Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera"));
	Camera->SetupAttachment(SpringArm);
}

void APS_Editor_Pawn::BeginPlay()
{
	Super::BeginPlay();
}

void APS_Editor_Pawn::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

	// Track Alt key state every frame
	if (APlayerController* PC = Cast<APlayerController>(GetController()))
	{
		bAltHeld = PC->IsInputKeyDown(EKeys::LeftAlt) || PC->IsInputKeyDown(EKeys::RightAlt);
	}
}

void APS_Editor_Pawn::CreateInputActions()
{
	// --- Create Input Actions ---

	IA_Move = NewObject<UInputAction>(this, TEXT("IA_Move"));
	IA_Move->ValueType = EInputActionValueType::Axis3D;
	IA_Move->bConsumeInput = false;

	IA_Look = NewObject<UInputAction>(this, TEXT("IA_Look"));
	IA_Look->ValueType = EInputActionValueType::Axis2D;

	IA_Scroll = NewObject<UInputAction>(this, TEXT("IA_Scroll"));
	IA_Scroll->ValueType = EInputActionValueType::Axis1D;

	IA_LeftClick = NewObject<UInputAction>(this, TEXT("IA_LeftClick"));
	IA_LeftClick->ValueType = EInputActionValueType::Boolean;

	IA_RightClick = NewObject<UInputAction>(this, TEXT("IA_RightClick"));
	IA_RightClick->ValueType = EInputActionValueType::Boolean;

	IA_MiddleClick = NewObject<UInputAction>(this, TEXT("IA_MiddleClick"));
	IA_MiddleClick->ValueType = EInputActionValueType::Boolean;

	// --- Create Mapping Context ---
	EditorMappingContext = NewObject<UInputMappingContext>(this, TEXT("EditorMappingContext"));

	// Movement helpers
	auto MapForward = [&](FKey Key)
	{
		FEnhancedActionKeyMapping& Mapping = EditorMappingContext->MapKey(IA_Move, Key);
		UInputModifierSwizzleAxis* Swizzle = NewObject<UInputModifierSwizzleAxis>(this);
		Swizzle->Order = EInputAxisSwizzle::YXZ;
		Mapping.Modifiers.Add(Swizzle);
	};

	auto MapBackward = [&](FKey Key)
	{
		FEnhancedActionKeyMapping& Mapping = EditorMappingContext->MapKey(IA_Move, Key);
		UInputModifierSwizzleAxis* Swizzle = NewObject<UInputModifierSwizzleAxis>(this);
		Swizzle->Order = EInputAxisSwizzle::YXZ;
		Mapping.Modifiers.Add(Swizzle);
		Mapping.Modifiers.Add(NewObject<UInputModifierNegate>(this));
	};

	auto MapRight = [&](FKey Key)
	{
		EditorMappingContext->MapKey(IA_Move, Key);
	};

	auto MapLeft = [&](FKey Key)
	{
		FEnhancedActionKeyMapping& Mapping = EditorMappingContext->MapKey(IA_Move, Key);
		Mapping.Modifiers.Add(NewObject<UInputModifierNegate>(this));
	};

	// ZQSD (AZERTY)
	MapForward(EKeys::Z);
	MapBackward(EKeys::S);
	MapRight(EKeys::D);
	MapLeft(EKeys::Q);

	// Arrow keys
	MapForward(EKeys::Up);
	MapBackward(EKeys::Down);
	MapRight(EKeys::Right);
	MapLeft(EKeys::Left);

	// E - Up (+Z)
	{
		FEnhancedActionKeyMapping& Mapping = EditorMappingContext->MapKey(IA_Move, EKeys::E);
		UInputModifierSwizzleAxis* Swizzle = NewObject<UInputModifierSwizzleAxis>(this);
		Swizzle->Order = EInputAxisSwizzle::ZYX;
		Mapping.Modifiers.Add(Swizzle);
	}

	// A - Down (-Z)
	{
		FEnhancedActionKeyMapping& Mapping = EditorMappingContext->MapKey(IA_Move, EKeys::A);
		UInputModifierSwizzleAxis* Swizzle = NewObject<UInputModifierSwizzleAxis>(this);
		Swizzle->Order = EInputAxisSwizzle::ZYX;
		Mapping.Modifiers.Add(Swizzle);
		Mapping.Modifiers.Add(NewObject<UInputModifierNegate>(this));
	}

	// Mouse
	EditorMappingContext->MapKey(IA_Look, EKeys::Mouse2D);
	EditorMappingContext->MapKey(IA_Scroll, EKeys::MouseWheelAxis);
	EditorMappingContext->MapKey(IA_LeftClick, EKeys::LeftMouseButton);
	EditorMappingContext->MapKey(IA_RightClick, EKeys::RightMouseButton);
	EditorMappingContext->MapKey(IA_MiddleClick, EKeys::MiddleMouseButton);
}

void APS_Editor_Pawn::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);

	CreateInputActions();

	if (APlayerController* PC = Cast<APlayerController>(GetController()))
	{
		if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PC->GetLocalPlayer()))
		{
			Subsystem->ClearAllMappings();
			Subsystem->AddMappingContext(EditorMappingContext, 0);
		}
	}

	UEnhancedInputComponent* EIC = CastChecked<UEnhancedInputComponent>(PlayerInputComponent);

	EIC->BindAction(IA_Move, ETriggerEvent::Triggered, this, &APS_Editor_Pawn::HandleMove);
	EIC->BindAction(IA_Look, ETriggerEvent::Triggered, this, &APS_Editor_Pawn::HandleLook);
	EIC->BindAction(IA_Scroll, ETriggerEvent::Triggered, this, &APS_Editor_Pawn::HandleScroll);

	EIC->BindAction(IA_LeftClick, ETriggerEvent::Started, this, &APS_Editor_Pawn::HandleLeftClickStarted);
	EIC->BindAction(IA_LeftClick, ETriggerEvent::Completed, this, &APS_Editor_Pawn::HandleLeftClickCompleted);

	EIC->BindAction(IA_RightClick, ETriggerEvent::Started, this, &APS_Editor_Pawn::HandleRightClickStarted);
	EIC->BindAction(IA_RightClick, ETriggerEvent::Completed, this, &APS_Editor_Pawn::HandleRightClickCompleted);

	EIC->BindAction(IA_MiddleClick, ETriggerEvent::Started, this, &APS_Editor_Pawn::HandleMiddleClickStarted);
	EIC->BindAction(IA_MiddleClick, ETriggerEvent::Completed, this, &APS_Editor_Pawn::HandleMiddleClickCompleted);
}

// ---- Input Handlers ----

void APS_Editor_Pawn::HandleMove(const FInputActionValue& Value)
{
	const FVector MoveInput = Value.Get<FVector>();
	const float DeltaTime = GetWorld()->GetDeltaSeconds();

	const FRotator Rotation = GetActorRotation();
	const FVector Forward = FRotationMatrix(Rotation).GetUnitAxis(EAxis::X);
	const FVector Right = FRotationMatrix(Rotation).GetUnitAxis(EAxis::Y);
	const FVector Up = FVector::UpVector;

	const FVector Movement = (Forward * MoveInput.Y) + (Right * MoveInput.X) + (Up * MoveInput.Z);
	AddActorWorldOffset(Movement * FlySpeed * DeltaTime, true);
}

void APS_Editor_Pawn::HandleLook(const FInputActionValue& Value)
{
	if (CameraMode == EPS_Editor_CameraMode::Idle)
	{
		return;
	}

	const FVector2D LookInput = Value.Get<FVector2D>();

	if (CameraMode == EPS_Editor_CameraMode::Pan)
	{
		// Left-click drag: mouse X = yaw rotation (Z axis only), mouse Y = move forward/back on horizontal plane
		const float YawDelta = LookInput.X * PanYawSensitivity;
		FRotator CurrentRotation = GetActorRotation();
		CurrentRotation.Yaw += YawDelta;
		SetActorRotation(CurrentRotation);

		// Forward/back movement on horizontal plane
		const FVector HorizontalForward = FVector(GetActorForwardVector().X, GetActorForwardVector().Y, 0.0f).GetSafeNormal();
		const FVector PanMovement = HorizontalForward * LookInput.Y * PanSpeed;
		AddActorWorldOffset(PanMovement, true);
	}
	else if (CameraMode == EPS_Editor_CameraMode::Fly)
	{
		// Right-click drag: full free look
		const float YawDelta = LookInput.X * LookSensitivity;
		const float PitchDelta = LookInput.Y * LookSensitivity;

		FRotator CurrentRotation = GetActorRotation();
		CurrentRotation.Yaw += YawDelta;
		CurrentRotation.Pitch = FMath::Clamp(CurrentRotation.Pitch + PitchDelta, -89.0f, 89.0f);
		CurrentRotation.Roll = 0.0f;
		SetActorRotation(CurrentRotation);
	}
	else if (CameraMode == EPS_Editor_CameraMode::Orbit)
	{
		OrbitYaw += LookInput.X * OrbitSensitivity;
		OrbitPitch = FMath::Clamp(OrbitPitch + (LookInput.Y * OrbitSensitivity), -89.0f, 89.0f);

		const FRotator OrbitRotation(OrbitPitch, OrbitYaw, 0.0f);
		const FVector Offset = OrbitRotation.Vector() * -OrbitDistance;

		SetActorLocation(OrbitFocalPoint + Offset);
		SetActorRotation((OrbitFocalPoint - GetActorLocation()).Rotation());
	}
}

void APS_Editor_Pawn::HandleScroll(const FInputActionValue& Value)
{
	const float ScrollValue = Value.Get<float>();

	if (CameraMode == EPS_Editor_CameraMode::Fly)
	{
		FlySpeed = FMath::Clamp(FlySpeed + ScrollValue * FlySpeedScrollStep, FlySpeedMin, FlySpeedMax);
	}
	else if (CameraMode == EPS_Editor_CameraMode::Orbit)
	{
		OrbitDistance = FMath::Clamp(OrbitDistance - ScrollValue * OrbitZoomStep, OrbitDistanceMin, OrbitDistanceMax);

		const FRotator OrbitRotation(OrbitPitch, OrbitYaw, 0.0f);
		const FVector Offset = OrbitRotation.Vector() * -OrbitDistance;
		SetActorLocation(OrbitFocalPoint + Offset);
	}
	else
	{
		// Idle or Pan: dolly forward/backward
		const FVector Forward = GetActorForwardVector();
		AddActorWorldOffset(Forward * ScrollValue * OrbitZoomStep * 5.0f, true);
	}
}

void APS_Editor_Pawn::HandleLeftClickStarted(const FInputActionValue& Value)
{
	if (bAltHeld)
	{
		// Alt + left-click = orbit
		ComputeOrbitFocalPoint();
		SetCameraMode(EPS_Editor_CameraMode::Orbit);
	}
	else
	{
		// Left-click = pan (yaw + forward/back on horizontal plane)
		SetCameraMode(EPS_Editor_CameraMode::Pan);
	}
}

void APS_Editor_Pawn::HandleLeftClickCompleted(const FInputActionValue& Value)
{
	if (CameraMode == EPS_Editor_CameraMode::Pan || CameraMode == EPS_Editor_CameraMode::Orbit)
	{
		SetCameraMode(EPS_Editor_CameraMode::Idle);
	}
}

void APS_Editor_Pawn::HandleRightClickStarted(const FInputActionValue& Value)
{
	SetCameraMode(EPS_Editor_CameraMode::Fly);
}

void APS_Editor_Pawn::HandleRightClickCompleted(const FInputActionValue& Value)
{
	if (CameraMode == EPS_Editor_CameraMode::Fly)
	{
		SetCameraMode(EPS_Editor_CameraMode::Idle);
	}
}

void APS_Editor_Pawn::HandleMiddleClickStarted(const FInputActionValue& Value)
{
	ComputeOrbitFocalPoint();
	SetCameraMode(EPS_Editor_CameraMode::Orbit);
}

void APS_Editor_Pawn::HandleMiddleClickCompleted(const FInputActionValue& Value)
{
	if (CameraMode == EPS_Editor_CameraMode::Orbit)
	{
		SetCameraMode(EPS_Editor_CameraMode::Idle);
	}
}

// ---- Helpers ----

void APS_Editor_Pawn::SetCameraMode(EPS_Editor_CameraMode NewMode)
{
	CameraMode = NewMode;

	if (NewMode == EPS_Editor_CameraMode::Orbit)
	{
		const FRotator CurrentRotation = GetActorRotation();
		OrbitPitch = CurrentRotation.Pitch;
		OrbitYaw = CurrentRotation.Yaw;
	}

	UpdateCursorVisibility();
}

void APS_Editor_Pawn::UpdateCursorVisibility()
{
	APlayerController* PC = Cast<APlayerController>(GetController());
	if (!PC)
	{
		return;
	}

	const bool bShowCursor = (CameraMode == EPS_Editor_CameraMode::Idle);
	PC->bShowMouseCursor = bShowCursor;

	if (bShowCursor)
	{
		PC->SetInputMode(FInputModeGameAndUI().SetHideCursorDuringCapture(false));
	}
	else
	{
		PC->SetInputMode(FInputModeGameOnly());
	}
}

void APS_Editor_Pawn::ComputeOrbitFocalPoint()
{
	const FVector Start = GetActorLocation();
	const FVector Forward = GetActorForwardVector();
	const FVector End = Start + Forward * OrbitDistance;

	FHitResult Hit;
	FCollisionQueryParams Params;
	Params.AddIgnoredActor(this);

	if (GetWorld()->LineTraceSingleByChannel(Hit, Start, End, ECC_Visibility, Params))
	{
		OrbitFocalPoint = Hit.ImpactPoint;
		OrbitDistance = FVector::Dist(Start, OrbitFocalPoint);
	}
	else
	{
		OrbitFocalPoint = End;
	}
}
