using System.Collections.ObjectModel;
using Avalonia.Threading;
using ClawdDotNet.App;
using ClawdDotNet.App.Models;
using ClawdDotNet.App.Services;
using ClawdDotNet.Core.Config;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using ClawdDotNet.Core.Scheduling;
using Microsoft.Extensions.Logging;

namespace ClawdDotNet.Desktop.ViewModels;

public sealed record JobDisplayItemViewModel(
    string JobType,
    string JobId,
    string AgentId,
    string AgentName,
    string ToolName,
    string CronExpression,
    string TaskMessage,
    bool RunOnStart,
    string NextRun,
    string LastRun,
    string LastStatus,
    string Status
);

/// <summary>Die drei Ansichten der Aufgaben-Seite.</summary>
public enum TasksTab
{
    Jobs,
    Services,
    History
}

public sealed record ServiceDisplayItemViewModel(
    string ServiceId,
    string Name,
    string Type,
    int Port,
    string Status,
    string Description
);

/// <summary>
/// Ansichtsmodell für die Aufgaben-Seite (Jobs, Dienste, Verlauf).
/// </summary>
public sealed partial class TasksPageViewModel : PageViewModel
{
    private readonly AppHost? _host;
    private readonly JobHistoryService? _jobHistoryService;
    private readonly ILogger? _logger;
    private readonly Dictionary<string, DateTime> _jobLastRunTimes = new();

    public ObservableCollection<JobDisplayItemViewModel> JobEntries { get; } = [];
    public ObservableCollection<ServiceDisplayItemViewModel> ServiceEntries { get; } = [];
    public ObservableCollection<JobHistoryEntry> JobHistoryEntries { get; } = [];

    [ObservableProperty]
    private JobDisplayItemViewModel? _selectedJob;

    [ObservableProperty]
    private ServiceDisplayItemViewModel? _selectedService;

    [ObservableProperty]
    private string _statusText = "Bereit";

    [ObservableProperty]
    private bool _isRunningJob;

    /// <summary>
    /// Der Entwurf ersetzt die Registerkarten durch eine Segmentleiste. Fachlich ist das
    /// dasselbe: eine von drei Ansichten. Die drei Kennzeichen darunter gibt es, weil
    /// eine Bindung „Ist dieser Wert gleich Services?" in AXAML sonst jedes Mal einen
    /// Konverter mit Parameter bräuchte.
    /// </summary>
    [ObservableProperty]
    [NotifyPropertyChangedFor(nameof(IsJobsTab))]
    [NotifyPropertyChangedFor(nameof(IsServicesTab))]
    [NotifyPropertyChangedFor(nameof(IsHistoryTab))]
    private TasksTab _selectedTab = TasksTab.Jobs;

    public bool IsJobsTab => SelectedTab == TasksTab.Jobs;
    public bool IsServicesTab => SelectedTab == TasksTab.Services;
    public bool IsHistoryTab => SelectedTab == TasksTab.History;

    public event Func<AddJobViewModel, Task<AddJobResultData?>>? RequestAddJobDialog;
    public event Func<AddServiceViewModel, Task<AddServiceResultData?>>? RequestAddServiceDialog;

    public TasksPageViewModel() : this(null) { }

    public TasksPageViewModel(AppHost? host) : base("Aufgaben")
    {
        _host = host;

        if (host is not null)
        {
            _jobHistoryService = new JobHistoryService(host.InstancePath);
            _logger = host.LoggerFactory.CreateLogger("ClawdDotNet.Desktop.Tasks");

            EnsureBuiltInServices();
            RefreshJobList();
            RefreshServiceList();
            RefreshJobHistory();
        }
        else
        {
            StatusText = "Entwurfsmodus";
        }
    }

    private void SaveInstanceConfig()
    {
        if (_host is null) return;
        _host.Directories.SaveInstanceConfig(_host.InstancePath, _host.Instance);
    }

    private void EnsureBuiltInServices()
    {
        if (_host is null) return;

        var defaults = BuiltInServices.CreateDefaults();
        var changed = false;

        foreach (var def in defaults)
        {
            if (_host.Instance.Services.Any(s => s.ServiceId == def.ServiceId))
                continue;

            _host.Instance.Services.Insert(0, def);
            changed = true;
        }

        if (changed)
            SaveInstanceConfig();
    }

    [RelayCommand]
    private void RefreshJobList()
    {
        JobEntries.Clear();
        if (_host is null) return;

        foreach (var agent in _host.Instance.Agents)
        {
            // Agent Wakeup Jobs
            if (agent.Scheduler is not null)
            {
                var nextRun = CalculateNextRun(agent.Scheduler.Cron);
                var agentLastRun = _jobLastRunTimes.TryGetValue($"agent_{agent.AgentId}", out var agentLastTime)
                    ? agentLastTime.ToString("yyyy-MM-dd HH:mm:ss")
                    : "—";

                JobEntries.Add(new JobDisplayItemViewModel(
                    "Agent Wakeup",
                    $"agent_{agent.AgentId}",
                    agent.AgentId,
                    agent.DisplayName,
                    "—",
                    agent.Scheduler.Cron,
                    agent.Scheduler.TaskMessage,
                    agent.Scheduler.RunOnStart,
                    nextRun,
                    agentLastRun,
                    "—",
                    _host.Scanner is not null ? "Aktiv" : "Inaktiv"
                ));
            }

            // Tool Jobs
            foreach (var toolJob in agent.ToolJobs)
            {
                var nextRun = CalculateNextRun(toolJob.Cron);
                var toolLastRun = _jobLastRunTimes.TryGetValue(toolJob.JobId, out var lastTime)
                    ? lastTime.ToString("yyyy-MM-dd HH:mm:ss")
                    : "—";

                JobEntries.Add(new JobDisplayItemViewModel(
                    "Tool Job",
                    toolJob.JobId,
                    agent.AgentId,
                    agent.DisplayName,
                    toolJob.ToolName,
                    toolJob.Cron,
                    toolJob.JobTypeId,
                    toolJob.RunOnStart,
                    nextRun,
                    toolLastRun,
                    "—",
                    toolJob.Enabled ? "Aktiv" : "Deaktiviert"
                ));
            }
        }
    }

    [RelayCommand]
    private void RefreshServiceList()
    {
        ServiceEntries.Clear();
        if (_host is null) return;

        foreach (var svc in _host.Instance.Services)
        {
            ServiceEntries.Add(new ServiceDisplayItemViewModel(
                svc.ServiceId,
                svc.Name,
                svc.Type,
                svc.Port,
                svc.Enabled ? "Bereit" : "Deaktiviert",
                svc.Description
            ));
        }
    }

    [RelayCommand]
    private void RefreshJobHistory()
    {
        JobHistoryEntries.Clear();
        if (_jobHistoryService is null) return;

        foreach (var entry in _jobHistoryService.GetAll().Take(200))
            JobHistoryEntries.Add(entry);
    }

    [RelayCommand]
    private async Task AddJobAsync()
    {
        if (_host is null || RequestAddJobDialog is null) return;

        var vm = new AddJobViewModel(_host.Instance.Agents);
        var result = await RequestAddJobDialog(vm);
        if (result is null) return;

        var agent = _host.Instance.Agents.FirstOrDefault(a => a.AgentId == result.AgentId);
        if (agent is null) return;

        if (result.IsToolJob)
        {
            agent.ToolJobs.Add(new ToolJobConfig
            {
                ToolName = result.ToolName,
                JobTypeId = result.JobTypeId,
                Cron = result.CronExpression,
                RunOnStart = result.RunOnStart,
                Enabled = true
            });

            _logger?.LogInformation("Tool-Job hinzugefügt: Agent={Agent}, Tool={Tool}, Cron={Cron}",
                agent.DisplayName, result.ToolName, result.CronExpression);
        }
        else
        {
            agent.Scheduler = new SchedulerConfig
            {
                Cron = result.CronExpression,
                RunOnStart = result.RunOnStart,
                TaskMessage = result.TaskMessage
            };

            _logger?.LogInformation("Agent-Wakeup-Job hinzugefügt: Agent={Agent}, Cron={Cron}",
                agent.DisplayName, result.CronExpression);
        }

        SaveInstanceConfig();
        RefreshJobList();
    }

    [RelayCommand]
    private async Task EditJobAsync()
    {
        if (_host is null || SelectedJob is null || RequestAddJobDialog is null) return;

        var entry = SelectedJob;
        var agent = _host.Instance.Agents.FirstOrDefault(a => a.AgentId == entry.AgentId);
        if (agent is null) return;

        var isToolJob = entry.JobType == "Tool Job";
        var vm = new AddJobViewModel(_host.Instance.Agents)
        {
            SelectedAgent = agent,
            IsToolJob = isToolJob,
            SelectedToolName = isToolJob ? entry.ToolName : "FileRW",
            JobTypeId = isToolJob ? entry.TaskMessage : "PollJob",
            CronExpression = entry.CronExpression,
            TaskMessage = isToolJob ? "" : entry.TaskMessage,
            RunOnStart = entry.RunOnStart
        };

        var result = await RequestAddJobDialog(vm);
        if (result is null) return;

        if (isToolJob)
        {
            var toolJob = agent.ToolJobs.FirstOrDefault(j => j.JobId == entry.JobId);
            if (toolJob is not null)
            {
                toolJob.Cron = result.CronExpression;
                toolJob.RunOnStart = result.RunOnStart;
                toolJob.JobTypeId = result.JobTypeId;
                toolJob.ToolName = result.ToolName;
            }
        }
        else
        {
            if (agent.Scheduler is not null)
            {
                agent.Scheduler.Cron = result.CronExpression;
                agent.Scheduler.TaskMessage = result.TaskMessage;
                agent.Scheduler.RunOnStart = result.RunOnStart;
            }
        }

        SaveInstanceConfig();
        RefreshJobList();
    }

    [RelayCommand]
    private void RemoveJob()
    {
        if (_host is null || SelectedJob is null) return;

        var entry = SelectedJob;
        var agent = _host.Instance.Agents.FirstOrDefault(a => a.AgentId == entry.AgentId);
        if (agent is null) return;

        if (entry.JobType == "Tool Job")
        {
            agent.ToolJobs.RemoveAll(j => j.JobId == entry.JobId);
        }
        else
        {
            agent.Scheduler = null;
        }

        SaveInstanceConfig();
        RefreshJobList();
    }

    [RelayCommand]
    private async Task RunJobNowAsync()
    {
        if (_host is null || SelectedJob is null) return;

        var entry = SelectedJob;
        var agent = _host.Instance.Agents.FirstOrDefault(a => a.AgentId == entry.AgentId);
        if (agent is null) return;

        IsRunningJob = true;
        StatusText = $"Führe Job für '{agent.DisplayName}' aus…";

        try
        {
            if (entry.JobType == "Tool Job")
            {
                if (_host.Scanner is null)
                {
                    StatusText = "Scanner ist nicht aktiv.";
                    return;
                }

                var jobConfig = agent.ToolJobs.FirstOrDefault(j => j.JobId == entry.JobId);
                if (jobConfig is null) return;

                var ran = await _host.Scanner.RunTaskNowAsync(
                    $"tj-{agent.AgentId}-{jobConfig.JobId}", CancellationToken.None);

                _jobLastRunTimes[jobConfig.JobId] = DateTime.Now;
                var info = ran ? "Ausgeführt" : "Nicht bereit";

                _jobHistoryService?.Add(new JobHistoryEntry
                {
                    JobName = jobConfig.JobTypeId,
                    Agent = agent.DisplayName,
                    Time = DateTime.Now,
                    JobDescription = $"Tool Job: {jobConfig.ToolName}",
                    Info = info,
                    Status = "Manual"
                });

                StatusText = $"Tool Job '{jobConfig.JobTypeId}' abgeschlossen ({info}).";
            }
            else
            {
                if (_host.Engine is null)
                {
                    StatusText = "Agent-Engine ist nicht aktiv (kein API-Key).";
                    return;
                }

                var taskMessage = agent.Scheduler?.TaskMessage ?? "Führe deine zugewiesenen Aufgaben aus.";
                var result = await _host.Engine.RunAsync(agent, taskMessage, _host.Instance.InstanceId, CancellationToken.None);

                _jobLastRunTimes[$"agent_{agent.AgentId}"] = DateTime.Now;

                _jobHistoryService?.Add(new JobHistoryEntry
                {
                    JobName = "Agent Wakeup",
                    Agent = agent.DisplayName,
                    Time = DateTime.Now,
                    JobDescription = taskMessage,
                    Info = $"{result.Status} ({result.StepCount} Steps, {result.TokensUsed:N0} Tokens)",
                    Status = "Manual"
                });

                StatusText = $"Agent '{agent.DisplayName}' abgeschlossen: {result.Status} ({result.TokensUsed:N0} Tokens).";
            }

            RefreshJobList();
            RefreshJobHistory();
        }
        catch (Exception ex)
        {
            StatusText = $"Fehler beim Ausführen: {ex.Message}";
            _logger?.LogError(ex, "Job-Ausführung fehlgeschlagen");
        }
        finally
        {
            IsRunningJob = false;
        }
    }

    [RelayCommand]
    private async Task AddServiceAsync()
    {
        if (_host is null || RequestAddServiceDialog is null) return;

        var vm = new AddServiceViewModel();
        var result = await RequestAddServiceDialog(vm);
        if (result is null) return;

        var svcConfig = new ServiceConfig
        {
            Name = result.ServiceName,
            Type = result.ServiceType,
            Port = result.ServicePort,
            Enabled = true,
            Description = result.ServiceDescription
        };

        _host.Instance.Services.Add(svcConfig);
        SaveInstanceConfig();
        RefreshServiceList();
    }

    [RelayCommand]
    private void RemoveService()
    {
        if (_host is null || SelectedService is null) return;

        _host.Instance.Services.RemoveAll(s => s.ServiceId == SelectedService.ServiceId);
        SaveInstanceConfig();
        RefreshServiceList();
    }

    /// <summary>
    /// Schaltet einen Job an oder ab, ohne ihn zu entfernen — im Entwurf die neue
    /// Schaltfläche „Aktivieren/Deaktivieren" in der Werkzeugleiste.
    ///
    /// <para>Gilt nur für Tool-Jobs: <c>SchedulerConfig</c> hat keinen solchen Schalter,
    /// ein Agent-Wakeup existiert oder existiert nicht. Ihn hier zu ergänzen wäre keine
    /// Oberflächenarbeit — der Scanner müsste den Schalter erst einmal lesen, sonst liefe
    /// ein „deaktivierter" Wakeup munter weiter.</para>
    /// </summary>
    [RelayCommand]
    private void ToggleJobEnabled()
    {
        if (_host is null || SelectedJob is null) return;

        var entry = SelectedJob;

        if (entry.JobType != "Tool Job")
        {
            StatusText = "Nur Tool-Jobs lassen sich abschalten. "
                         + "Ein Agent-Wakeup wird über die Agenten-Seite entfernt.";
            return;
        }

        var agent = _host.Instance.Agents.FirstOrDefault(a => a.AgentId == entry.AgentId);
        var job = agent?.ToolJobs.FirstOrDefault(j => j.JobId == entry.JobId);
        if (job is null) return;

        job.Enabled = !job.Enabled;
        SaveInstanceConfig();
        RefreshJobList();

        // Die Auswahl geht beim Neuaufbau der Liste verloren — sie wieder zu setzen
        // erspart es, für zwei Schaltvorgänge zweimal dieselbe Zeile anzuklicken.
        SelectedJob = JobEntries.FirstOrDefault(j => j.JobId == entry.JobId);

        StatusText = job.Enabled
            ? $"Job '{entry.TaskMessage}' aktiviert."
            : $"Job '{entry.TaskMessage}' deaktiviert.";
    }

    /// <summary>
    /// Aktiviert bzw. deaktiviert einen Dienst.
    ///
    /// <para>Bewusst nicht „Start"/„Stop" genannt, auch wenn der Entwurf das so zeigt:
    /// Umgelegt wird das Kennzeichen <c>Enabled</c> in der Instanzkonfiguration. Ein
    /// laufender Dienstprozess wird davon nicht angehalten — eine Schaltfläche „Stop",
    /// nach der etwas weiterläuft, wäre schlimmer als gar keine.</para>
    /// </summary>
    [RelayCommand]
    private void ToggleServiceEnabled()
    {
        if (_host is null || SelectedService is null) return;

        var id = SelectedService.ServiceId;
        var service = _host.Instance.Services.FirstOrDefault(s => s.ServiceId == id);
        if (service is null) return;

        service.Enabled = !service.Enabled;
        SaveInstanceConfig();
        RefreshServiceList();

        SelectedService = ServiceEntries.FirstOrDefault(s => s.ServiceId == id);

        StatusText = service.Enabled
            ? $"Dienst '{service.Name}' aktiviert."
            : $"Dienst '{service.Name}' deaktiviert.";
    }

    [RelayCommand]
    private void SelectTab(string? tab)
    {
        SelectedTab = tab switch
        {
            "Services" => TasksTab.Services,
            "History" => TasksTab.History,
            _ => TasksTab.Jobs
        };
    }

    private static string CalculateNextRun(string? cronExpr)
    {
        if (string.IsNullOrWhiteSpace(cronExpr)) return "—";
        try
        {
            var cron = CronExpression.Parse(cronExpr);
            var next = cron.GetNextOccurrence(DateTime.UtcNow);
            return next?.ToLocalTime().ToString("yyyy-MM-dd HH:mm") ?? "—";
        }
        catch
        {
            return "Ungültig";
        }
    }
}
