using ClawdDotNet.Core.Config;
using ClawdDotNet.Core.Scheduling;
using ClawdDotNet.Core.Tools;

namespace ClawdDotNet;

public sealed partial class frm_AddJob : Form
{
    private readonly IReadOnlyList<AgentConfig> _agents;
    private readonly IReadOnlyList<(IAgentTool Tool, IToolJobProvider Provider)> _jobProviders;

    // ─── Output Properties ───

    public bool IsToolJob => cbJobType.SelectedIndex == 1;
    public string SelectedAgentId => (cbAgent.SelectedItem as AgentComboItem)?.AgentId ?? "";
    public string CronExpression => txtCron.Text.Trim();
    public string TaskMessage => txtTask.Text.Trim();
    public bool RunOnStart => chkRunOnStart.Checked;
    public string SelectedToolName => (cbTool.SelectedItem as ToolComboItem)?.ToolName ?? "";
    public string SelectedJobTypeId => (cbJobDef.SelectedItem as JobDefComboItem)?.JobTypeId ?? "";

    /// <summary>Konstruktor für "Hinzufügen"</summary>
    public frm_AddJob(IReadOnlyList<AgentConfig> agents, ToolRegistry toolRegistry)
    {
        InitializeComponent();

        _agents = agents;
        _jobProviders = toolRegistry.GetJobProviders();

        foreach (var agent in agents)
            cbAgent.Items.Add(new AgentComboItem(agent.AgentId, agent.DisplayName));
        if (cbAgent.Items.Count > 0) cbAgent.SelectedIndex = 0;

        foreach (var (tool, _) in _jobProviders)
            cbTool.Items.Add(new ToolComboItem(tool.Name, tool.Description));
        if (cbTool.Items.Count > 0) cbTool.SelectedIndex = 0;

        cbJobType.SelectedIndex = 0;
    }

    /// <summary>Konstruktor für "Bearbeiten" — Agent, Tool und Job-Typ sind gesperrt</summary>
    public frm_AddJob(IReadOnlyList<AgentConfig> agents, ToolRegistry toolRegistry,
        string agentId, string cron, string taskMessage, bool runOnStart,
        bool isToolJob, string? toolName = null, string? jobTypeId = null)
        : this(agents, toolRegistry)
    {
        Text = "Job bearbeiten";
        btnOk.Text = "Speichern";

        // Job-Typ setzen und sperren
        cbJobType.SelectedIndex = isToolJob ? 1 : 0;
        cbJobType.Enabled = false;

        // Agent vorauswählen und sperren
        for (int i = 0; i < cbAgent.Items.Count; i++)
        {
            if (((AgentComboItem)cbAgent.Items[i]!).AgentId == agentId)
            {
                cbAgent.SelectedIndex = i;
                break;
            }
        }
        cbAgent.Enabled = false;

        // Bearbeitbare Felder befüllen
        txtCron.Text = cron;
        chkRunOnStart.Checked = runOnStart;

        if (isToolJob && toolName is not null)
        {
            // Tool vorauswählen und sperren
            for (int i = 0; i < cbTool.Items.Count; i++)
            {
                if (((ToolComboItem)cbTool.Items[i]!).ToolName == toolName)
                {
                    cbTool.SelectedIndex = i;
                    break;
                }
            }
            cbTool.Enabled = false;

            // Job-Definition vorauswählen und sperren
            if (jobTypeId is not null)
            {
                for (int i = 0; i < cbJobDef.Items.Count; i++)
                {
                    if (((JobDefComboItem)cbJobDef.Items[i]!).JobTypeId == jobTypeId)
                    {
                        cbJobDef.SelectedIndex = i;
                        break;
                    }
                }
            }
            cbJobDef.Enabled = false;
        }
        else
        {
            txtTask.Text = taskMessage;
        }
    }

    private void OnJobTypeChanged(object? sender, EventArgs e)
    {
        var isToolJob = cbJobType.SelectedIndex == 1;

        lblTool.Visible = isToolJob;
        cbTool.Visible = isToolJob;
        lblJobDef.Visible = isToolJob;
        cbJobDef.Visible = isToolJob;

        lblTask.Visible = !isToolJob;
        txtTask.Visible = !isToolJob;

        RefreshAgentList();
    }

    private void OnToolChanged(object? sender, EventArgs e)
    {
        cbJobDef.Items.Clear();

        if (cbTool.SelectedItem is not ToolComboItem toolItem)
            return;

        var provider = _jobProviders.FirstOrDefault(p => ((IAgentTool)p.Provider).Name == toolItem.ToolName);
        if (provider.Provider is null)
            return;

        foreach (var def in provider.Provider.GetJobDefinitions())
            cbJobDef.Items.Add(new JobDefComboItem(def.JobTypeId, def.DisplayName, def.Description));

        if (cbJobDef.Items.Count > 0) cbJobDef.SelectedIndex = 0;

        RefreshAgentList();
    }

    private void RefreshAgentList()
    {
        var previousAgentId = (cbAgent.SelectedItem as AgentComboItem)?.AgentId;
        cbAgent.Items.Clear();

        var toolName = IsToolJob && cbTool.SelectedItem is ToolComboItem toolItem
            ? toolItem.ToolName : null;

        foreach (var agent in _agents)
        {
            if (toolName is not null && !agent.Tools.ContainsKey(toolName))
                continue;

            cbAgent.Items.Add(new AgentComboItem(agent.AgentId, agent.DisplayName));
        }

        if (previousAgentId is not null)
        {
            for (int i = 0; i < cbAgent.Items.Count; i++)
            {
                if (((AgentComboItem)cbAgent.Items[i]!).AgentId == previousAgentId)
                {
                    cbAgent.SelectedIndex = i;
                    break;
                }
            }
        }

        if (cbAgent.SelectedIndex < 0 && cbAgent.Items.Count > 0)
            cbAgent.SelectedIndex = 0;
    }

    private void OnCronChanged(object? sender, EventArgs e)
    {
        var text = txtCron.Text.Trim();
        if (string.IsNullOrWhiteSpace(text))
        {
            lblPreview.Text = "";
            return;
        }

        try
        {
            var cron = Core.Scheduling.CronExpression.Parse(text);
            var next = cron.GetNextOccurrence(DateTime.Now);
            lblPreview.Text = next is not null
                ? $"Nächste Ausführung: {next:dd.MM.yyyy HH:mm}"
                : "Kein nächster Zeitpunkt";
            lblPreview.ForeColor = Color.Green;
        }
        catch
        {
            lblPreview.Text = "Ungültiger Cron-Ausdruck";
            lblPreview.ForeColor = Color.Red;
        }
    }

    private void OnOkClick(object? sender, EventArgs e)
    {
        if (cbAgent.SelectedItem is null)
        {
            MessageBox.Show("Bitte einen Agenten auswählen.", "Validierung",
                MessageBoxButtons.OK, MessageBoxIcon.Warning);
            DialogResult = DialogResult.None;
            return;
        }

        if (string.IsNullOrWhiteSpace(txtCron.Text))
        {
            MessageBox.Show("Bitte einen Cron-Ausdruck eingeben.", "Validierung",
                MessageBoxButtons.OK, MessageBoxIcon.Warning);
            DialogResult = DialogResult.None;
            return;
        }

        try
        {
            Core.Scheduling.CronExpression.Parse(txtCron.Text.Trim());
        }
        catch
        {
            MessageBox.Show("Ungültiger Cron-Ausdruck.", "Validierung",
                MessageBoxButtons.OK, MessageBoxIcon.Warning);
            DialogResult = DialogResult.None;
            return;
        }

        if (IsToolJob)
        {
            if (cbTool.SelectedItem is null)
            {
                MessageBox.Show("Bitte ein Tool auswählen.", "Validierung",
                    MessageBoxButtons.OK, MessageBoxIcon.Warning);
                DialogResult = DialogResult.None;
                return;
            }

            if (cbJobDef.SelectedItem is null)
            {
                MessageBox.Show("Bitte einen Job-Typ auswählen.", "Validierung",
                    MessageBoxButtons.OK, MessageBoxIcon.Warning);
                DialogResult = DialogResult.None;
            }
        }
    }

    // ─── ComboBox Items ───

    private sealed record AgentComboItem(string AgentId, string DisplayName)
    {
        public override string ToString() => DisplayName;
    }

    private sealed record ToolComboItem(string ToolName, string Description)
    {
        public override string ToString() => ToolName;
    }

    private sealed record JobDefComboItem(string JobTypeId, string DisplayName, string Description)
    {
        public override string ToString() => $"{DisplayName} ({JobTypeId})";
    }
}
