package ee.futur.easygiantsfoundry;

import ee.futur.easygiantsfoundry.enums.Heat;
import ee.futur.easygiantsfoundry.enums.MetalBarType;
import ee.futur.easygiantsfoundry.enums.Stage;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Singleton;
import net.runelite.api.Client;
import net.runelite.client.ui.ColorScheme;
import net.runelite.client.ui.overlay.OverlayPanel;
import net.runelite.client.ui.overlay.OverlayPosition;
import net.runelite.client.ui.overlay.components.LineComponent;
import net.runelite.client.ui.overlay.components.TitleComponent;

@Singleton
public class FoundryOverlay2D extends OverlayPanel
{
	private static final int REGION_ID = 13491;
	private static final int PANEL_WIDTH = 240;

	private final Client client;
	private final EasyGiantsFoundryPlugin plugin;
	private final EasyGiantsFoundryState state;
	private final MetalBarCounter metalBarCounter;
	private final EasyGiantsFoundryConfig config;

	@Inject
	private FoundryOverlay2D(
		Client client,
		EasyGiantsFoundryPlugin plugin,
		EasyGiantsFoundryState state,
		MetalBarCounter metalBarCounter,
		EasyGiantsFoundryConfig config)
	{
		this.client = client;
		this.plugin = plugin;
		this.state = state;
		this.metalBarCounter = metalBarCounter;
		this.config = config;
		setPosition(OverlayPosition.BOTTOM_LEFT);
	}

	@Override
	public Dimension render(Graphics2D graphics)
	{
		if (!config.alwaysDrawInfoPanel() && client.getLocalPlayer().getWorldLocation().getRegionID() != REGION_ID)
		{
			return null;
		}

		panelComponent.getChildren().clear();
		panelComponent.setPreferredSize(new Dimension(PANEL_WIDTH, 0));

		boolean hasContent = false;
		if (config.drawTitle())
		{
			panelComponent.getChildren().add(TitleComponent.builder().text("Easy Giants' Foundry").build());
			hasContent = true;
		}

		boolean swordPickedUp = state.isEnabled() && state.getCurrentStage() != null;
		if (swordPickedUp)
		{
			hasContent = appendSection(buildStatusLines(), hasContent);
			hasContent = appendSection(buildProgressLines(), hasContent);
			hasContent = appendSection(buildHeatPlannerLines(), hasContent);
		}

		hasContent = appendSection(buildReputationLines(), hasContent);
		hasContent = appendSection(buildMetalLines(), hasContent);

		if (!hasContent)
		{
			return null;
		}

		return super.render(graphics);
	}

	private boolean appendSection(List<LineComponent> lines, boolean hasPreviousContent)
	{
		if (lines.isEmpty())
		{
			return hasPreviousContent;
		}

		if (hasPreviousContent)
		{
			panelComponent.getChildren().add(LineComponent.builder().left(" ").right("").build());
		}

		panelComponent.getChildren().addAll(lines);
		return true;
	}

	private List<LineComponent> buildStatusLines()
	{
		List<LineComponent> lines = new ArrayList<>();
		Heat heat = state.getCurrentHeat();
		Stage stage = state.getCurrentStage();

		if (config.drawHeatInfo())
		{
			lines.add(
				LineComponent.builder()
					.left("Heat")
					.right(heat.getName() + " (" + state.getHeatAmount() / 10 + "%)")
					.rightColor(heat.getColor())
					.build()
			);
		}

		if (config.drawStageInfo())
		{
			lines.add(
				LineComponent.builder()
					.left("Stage")
					.right(stage.getName() + " (" + state.getProgressAmount() / 10 + "%)")
					.rightColor(stage.getHeat().getColor())
					.build()
			);
		}

		return lines;
	}

	private List<LineComponent> buildProgressLines()
	{
		List<LineComponent> lines = new ArrayList<>();
		int actionsLeft = state.getActionsLeftInStage();
		int heatLeft = state.getActionsForHeatLevel();

		if (config.drawActionsLeft())
		{
			lines.add(LineComponent.builder().left("Actions left").right(Integer.toString(actionsLeft)).build());
		}

		if (config.drawHeatLeft())
		{
			lines.add(
				LineComponent.builder()
					.left("Heat left")
					.right(Integer.toString(heatLeft))
					.rightColor(getHeatColor(actionsLeft, heatLeft))
					.build()
			);
		}

		if (config.drawBonusActions())
		{
			lines.add(
				LineComponent.builder()
					.left("Bonus actions")
					.right(state.getBonusActionsReceived() + "/" + state.getBonusActionsExpected())
					.rightColor(getBonusActionsColor(state.getBonusActionsReceived(), state.getBonusActionsExpected()))
					.build()
			);
		}

		return lines;
	}

	private List<LineComponent> buildHeatPlannerLines()
	{
		List<LineComponent> lines = new ArrayList<>();
		if (!config.drawHeatDelta() && !config.drawHeatPlan() && !config.drawHeatActionTimer())
		{
			return lines;
		}

		if (state.getCurrentStage() == null)
		{
			return lines;
		}

		int heatChangeNeeded = state.getHeatChangeNeeded();

		if (config.drawHeatDelta())
		{
			lines.add(buildHeatDeltaLine(heatChangeNeeded));
		}

		if (config.drawHeatPlan())
		{
			lines.addAll(buildHeatPlanLines(heatChangeNeeded));
		}

		if (config.drawHeatActionTimer())
		{
			LineComponent timerLine = buildHeatActionTimerLine();
			if (timerLine != null)
			{
				lines.add(timerLine);
			}
		}

		return lines;
	}

	private LineComponent buildHeatDeltaLine(int heatChangeNeeded)
	{
		String location;
		if (heatChangeNeeded > 0)
		{
			location = "Lava";
		}
		else if (heatChangeNeeded < 0)
		{
			location = "Waterfall";
		}
		else
		{
			location = "In range";
		}

		String deltaText = heatChangeNeeded == 0
			? "0 (in range)"
			: String.format("%+d (%s)", heatChangeNeeded, location);

		Color color = heatChangeNeeded == 0 ? ColorScheme.PROGRESS_COMPLETE_COLOR : config.lavaWaterfallColour();

		return LineComponent.builder()
			.left("Heat Δ")
			.right(deltaText)
			.rightColor(color)
			.build();
	}

	private List<LineComponent> buildHeatPlanLines(int heatChangeNeeded)
	{
		List<LineComponent> lines = new ArrayList<>();

		if (heatChangeNeeded == 0)
		{
			lines.add(
				LineComponent.builder()
					.left("Heat plan")
					.right("Hold")
					.rightColor(ColorScheme.PROGRESS_COMPLETE_COLOR)
					.build()
			);
			return lines;
		}

		boolean isHeating = heatChangeNeeded > 0;
		HeatActionSolver.DurationResult fast = solveHeatAction(true, isHeating);
		HeatActionSolver.DurationResult slow = solveHeatAction(false, isHeating);
		boolean added = false;

		if (fast != null && fast.getDuration() > 0)
		{
			lines.add(buildHeatPlanLine("Fast", isHeating, true, fast));
			added = true;
		}

		if (slow != null && slow.getDuration() > 0)
		{
			lines.add(buildHeatPlanLine("Slow", isHeating, false, slow));
			added = true;
		}

		if (!added)
		{
			lines.add(
				LineComponent.builder()
					.left("Heat plan")
					.right("Hold")
					.rightColor(ColorScheme.PROGRESS_COMPLETE_COLOR)
					.build()
			);
		}

		return lines;
	}

	private LineComponent buildHeatPlanLine(String label, boolean isHeating, boolean fast, HeatActionSolver.DurationResult result)
	{
		String actionName = getActionName(isHeating, fast);
		String value = String.format("%s %dt → %s%s",
			actionName,
			result.getDuration(),
			formatPredictedHeat(result.getPredictedHeat()),
			formatPlanStatus(result));

		return LineComponent.builder()
			.left(label)
			.right(value)
			.rightColor(config.lavaWaterfallColour())
			.build();
	}

	private HeatActionSolver.DurationResult solveHeatAction(boolean fast, boolean isHeating)
	{
		Stage stage = state.getCurrentStage();
		if (stage == null)
		{
			return null;
		}

		return HeatActionSolver.solve(
			stage,
			state.getCurrentHeatRange(),
			state.getActionsLeftInStage(),
			state.getHeatAmount(),
			fast,
			isHeating,
			config.heatActionPadTicks(),
			state.isPlayerRunning()
		);
	}

	private LineComponent buildHeatActionTimerLine()
	{
		HeatActionStateMachine machine = state.heatActionStateMachine;
		if (machine.isIdle() || machine.getActionname() == null)
		{
			return null;
		}

		return LineComponent.builder()
			.left("Action")
			.right(String.format(
				"%s %dt → %s%s",
				prettifyActionName(machine.getActionname()),
				machine.getRemainingDuration(),
				formatPredictedHeat(machine.getPredictedHeat()),
				machine.isGoalInRange() ? "" : machine.isOverShooting() ? " (overshoot)" : " (limit)"))
			.rightColor(config.lavaWaterfallColour())
			.build();
	}

	private String formatPredictedHeat(int predictedHeat)
	{
		return heatPercent(predictedHeat) + "%";
	}

	private String formatPlanStatus(HeatActionSolver.DurationResult result)
	{
		if (result == null || result.isGoalInRange())
		{
			return "";
		}

		return result.isOvershooting() ? " (overshoot)" : " (limit)";
	}

	private String getActionName(boolean isHeating, boolean fast)
	{
		if (isHeating)
		{
			return fast ? "Dunk" : "Heat";
		}
		return fast ? "Quench" : "Cool";
	}

	private String prettifyActionName(String action)
	{
		if (action == null || action.isEmpty())
		{
			return "";
		}

		switch (action)
		{
			case "dunks":
				return "Dunk";
			case "heats":
				return "Heat";
			case "cools":
				return "Cool";
			case "quenches":
				return "Quench";
			default:
				return Character.toUpperCase(action.charAt(0)) + action.substring(1);
		}
	}

	private int heatPercent(int heat)
	{
		return Math.max(0, Math.min(1000, heat)) / 10;
	}

	private List<LineComponent> buildReputationLines()
	{
		List<LineComponent> lines = new ArrayList<>();
		if (!config.drawShopPoints())
		{
			return lines;
		}

		lines.add(LineComponent.builder().left("Reputation").right(Integer.toString(plugin.getReputation())).build());
		return lines;
	}

	private List<LineComponent> buildMetalLines()
	{
		List<LineComponent> lines = new ArrayList<>();
		if (!config.drawMetals())
		{
			return lines;
		}

		if (!metalBarCounter.isSeenBank())
		{
			lines.add(
				LineComponent.builder()
					.left("Metals: open bank")
					.leftColor(Color.RED)
					.build()
			);
		}

		addMetalCount(lines, "Bronze bars:", metalBarCounter.get(MetalBarType.BRONZE));
		addMetalCount(lines, "Iron bars:", metalBarCounter.get(MetalBarType.IRON));
		addMetalCount(lines, "Steel bars:", metalBarCounter.get(MetalBarType.STEEL));
		addMetalCount(lines, "Mithril bars:", metalBarCounter.get(MetalBarType.MITHRIL));
		addMetalCount(lines, "Adamant bars:", metalBarCounter.get(MetalBarType.ADAMANT));
		addMetalCount(lines, "Runite bars:", metalBarCounter.get(MetalBarType.RUNITE));

		return lines;
	}

	private void addMetalCount(List<LineComponent> lines, String displayName, int count)
	{
		if (count > 0 || config.drawAllMetals())
		{
			lines.add(LineComponent.builder().left(displayName).right(Integer.toString(count)).build());
		}
	}

	private Color getHeatColor(int actions, int heat)
	{
		if (heat >= actions)
		{
			return ColorScheme.PROGRESS_COMPLETE_COLOR;
		}

		if (heat > 0)
		{
			return ColorScheme.PROGRESS_INPROGRESS_COLOR;
		}

		return ColorScheme.PROGRESS_ERROR_COLOR;
	}

	private Color getBonusActionsColor(int received, int expected)
	{
		if (received >= expected)
		{
			return ColorScheme.PROGRESS_COMPLETE_COLOR;
		}

		return ColorScheme.PROGRESS_INPROGRESS_COLOR;
	}
}
