using System;
using System.Drawing;
using System.IO;
using System.Windows.Forms;

namespace TeslaConsole;

/// <summary>
/// Defines a new catalog product (a single launch entry) and, optionally, an install
/// package to deploy. The validated result is exposed as <see cref="Product"/>; the
/// caller persists it to the catalog and (optionally) pushes it to selected pods.
/// </summary>
internal class dlgAddProduct : Form
{
	private TextBox txtName;

	private TextBox txtInstallDir;

	private TextBox txtExe;

	private TextBox txtArgs;

	private TextBox txtPackage;

	private CheckBox chkAutoRestart;

	private CheckBox chkPushNow;

	private Button cmdOK;

	private Button cmdCancel;

	/// <summary>The product the operator defined. Valid only after the dialog returns <see cref="DialogResult.OK"/>.</summary>
	public ProductDefinition Product { get; private set; }

	/// <summary>Optional install package (zip) to deploy with the entry; null/empty for entry-only.</summary>
	public string PackagePath => string.IsNullOrWhiteSpace(txtPackage.Text) ? null : txtPackage.Text.Trim();

	/// <summary>Whether to push the new product to the currently selected pods after adding it.</summary>
	public bool PushNow => chkPushNow.Checked;

	public dlgAddProduct()
	{
		InitializeComponent();
	}

	private void InitializeComponent()
	{
		this.txtName = new TextBox();
		this.txtInstallDir = new TextBox();
		this.txtExe = new TextBox();
		this.txtArgs = new TextBox();
		this.txtPackage = new TextBox();
		this.chkAutoRestart = new CheckBox();
		this.chkPushNow = new CheckBox();
		this.cmdOK = new Button();
		this.cmdCancel = new Button();
		base.SuspendLayout();

		int labelX = 12;
		int fieldX = 130;
		int fieldW = 300;
		int browseX = fieldX + fieldW + 6;
		int y = 15;
		int rowH = 28;

		AddLabel("Product name:", labelX, y);
		this.txtName.SetBounds(fieldX, y - 3, fieldW, 20);
		y += rowH;

		AddLabel("Install directory:", labelX, y);
		this.txtInstallDir.SetBounds(fieldX, y - 3, fieldW, 20);
		AddBrowseButton(browseX, y - 4, BrowseInstallDir);
		y += rowH;

		AddLabel("Executable:", labelX, y);
		this.txtExe.SetBounds(fieldX, y - 3, fieldW, 20);
		AddBrowseButton(browseX, y - 4, BrowseExe);
		y += rowH;

		AddLabel("Arguments:", labelX, y);
		this.txtArgs.SetBounds(fieldX, y - 3, fieldW, 20);
		y += rowH;

		AddLabel("Install package (optional):", labelX, y);
		this.txtPackage.SetBounds(fieldX, y - 3, fieldW, 20);
		AddBrowseButton(browseX, y - 4, BrowsePackage);
		y += rowH;

		this.chkAutoRestart.SetBounds(fieldX, y, 200, 20);
		this.chkAutoRestart.Text = "Auto-restart if it exits";
		this.chkAutoRestart.Checked = true;
		y += 24;

		this.chkPushNow.SetBounds(fieldX, y, 280, 20);
		this.chkPushNow.Text = "Push to selected pods now";
		this.chkPushNow.Checked = true;
		y += 36;

		this.cmdOK.SetBounds(browseX - 169, y, 80, 24);
		this.cmdOK.Text = "OK";
		this.cmdOK.Click += cmdOK_Click;

		this.cmdCancel.SetBounds(browseX - 83, y, 80, 24);
		this.cmdCancel.Text = "Cancel";
		this.cmdCancel.DialogResult = DialogResult.Cancel;

		base.AcceptButton = this.cmdOK;
		base.CancelButton = this.cmdCancel;
		base.FormBorderStyle = FormBorderStyle.FixedDialog;
		base.MaximizeBox = false;
		base.MinimizeBox = false;
		base.StartPosition = FormStartPosition.CenterParent;
		base.ClientSize = new Size(browseX + 40, y + 36);
		base.Controls.Add(this.txtName);
		base.Controls.Add(this.txtInstallDir);
		base.Controls.Add(this.txtExe);
		base.Controls.Add(this.txtArgs);
		base.Controls.Add(this.txtPackage);
		base.Controls.Add(this.chkAutoRestart);
		base.Controls.Add(this.chkPushNow);
		base.Controls.Add(this.cmdOK);
		base.Controls.Add(this.cmdCancel);
		base.Name = "dlgAddProduct";
		this.Text = "Add Product";
		base.ResumeLayout(false);
		base.PerformLayout();
	}

	private void AddLabel(string text, int x, int y)
	{
		Label label = new Label
		{
			Text = text,
			AutoSize = true
		};
		label.SetBounds(x, y, 110, 16);
		base.Controls.Add(label);
	}

	private void AddBrowseButton(int x, int y, EventHandler onClick)
	{
		Button button = new Button
		{
			Text = "..."
		};
		button.SetBounds(x, y, 30, 22);
		button.Click += onClick;
		base.Controls.Add(button);
	}

	private void BrowseInstallDir(object sender, EventArgs e)
	{
		using FolderBrowserDialog dialog = new FolderBrowserDialog();
		if (!string.IsNullOrWhiteSpace(txtInstallDir.Text))
		{
			dialog.SelectedPath = txtInstallDir.Text;
		}
		if (dialog.ShowDialog(this) == DialogResult.OK)
		{
			txtInstallDir.Text = dialog.SelectedPath;
		}
	}

	private void BrowseExe(object sender, EventArgs e)
	{
		using OpenFileDialog dialog = new OpenFileDialog
		{
			Filter = "Executables (*.exe)|*.exe|All Files (*.*)|*.*",
			Title = "Select Executable"
		};
		if (dialog.ShowDialog(this) == DialogResult.OK)
		{
			txtExe.Text = dialog.FileName;
		}
	}

	private void BrowsePackage(object sender, EventArgs e)
	{
		using OpenFileDialog dialog = new OpenFileDialog
		{
			Filter = "VWE Install Archive (*.zip)|*.zip|All Files (*.*)|*.*",
			Title = "Select Install Package"
		};
		if (dialog.ShowDialog(this) == DialogResult.OK)
		{
			txtPackage.Text = dialog.FileName;
		}
	}

	private void cmdOK_Click(object sender, EventArgs e)
	{
		string name = txtName.Text.Trim();
		string installDir = txtInstallDir.Text.Trim();
		string exe = txtExe.Text.Trim();

		if (name.Length == 0)
		{
			Warn("Please enter a product name.");
			return;
		}
		if (exe.Length == 0)
		{
			Warn("Please specify the executable path.");
			return;
		}
		if (installDir.Length == 0)
		{
			Warn("Please specify the install directory.");
			return;
		}

		string fullExe, fullDir;
		try
		{
			fullExe = Path.GetFullPath(exe);
			fullDir = Path.GetFullPath(installDir);
		}
		catch (Exception)
		{
			Warn("The executable or install directory path is not valid.");
			return;
		}

		// Invariant: the exe must live under the install directory the package extracts to.
		string dirWithSep = fullDir.TrimEnd('\\', '/') + "\\";
		if (!fullExe.StartsWith(dirWithSep, StringComparison.OrdinalIgnoreCase))
		{
			Warn("The executable must be located inside the install directory:\n" + fullDir);
			return;
		}

		Guid id = Guid.NewGuid();
		Product = new ProductDefinition
		{
			Id = id,
			Name = name,
			MenuText = name + "...",
			HostTypeDialog = false
		};
		Product.Entries.Add(new LaunchEntryDefinition
		{
			LaunchKey = id,
			DisplayName = name,
			Exe = fullExe,
			Args = txtArgs.Text.Trim(),
			WorkingDirectory = fullDir,
			AutoRestart = chkAutoRestart.Checked,
			HostType = AppHostType.None
		});

		base.DialogResult = DialogResult.OK;
		Close();
	}

	private void Warn(string message)
	{
		MessageBox.Show(this, message, "Add Product", MessageBoxButtons.OK, MessageBoxIcon.Warning);
	}
}
