using System;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.ComponentModel;
using ClaudeDo.Ui.Services;
using Iciclecreek.Terminal;

namespace ClaudeDo.Ui.ViewModels;

/// <summary>
/// Hosts a <see cref="PtyTerminalSession"/> for an embedded ConPTY terminal. The view attaches
/// its <see cref="TerminalControl"/> once loaded via <see cref="AttachControl"/>; <see cref="Start"/>
/// can be called before or after attach — whichever happens second triggers the launch.
/// </summary>
public sealed partial class InteractiveTerminalViewModel : ViewModelBase, IDisposable
{
    private readonly PtyTerminalSession _session = new();
    private TerminalControl? _control;
    private TerminalLaunchDescriptor? _pendingDescriptor;

    [ObservableProperty] private bool _isRunning;
    [ObservableProperty] private bool _hasExited;
    [ObservableProperty] private int? _exitCode;
    [ObservableProperty] private string? _startError;

    public InteractiveTerminalViewModel()
    {
        _session.ProcessExited += OnSessionProcessExited;
    }

    /// <summary>Called by the view once its <see cref="TerminalControl"/> has loaded (template applied).</summary>
    public void AttachControl(TerminalControl control)
    {
        _control = control;
        if (_pendingDescriptor is { } descriptor)
        {
            _pendingDescriptor = null;
            _ = StartCoreAsync(descriptor);
        }
    }

    public void Start(TerminalLaunchDescriptor descriptor)
    {
        if (_control is null)
        {
            _pendingDescriptor = descriptor;
            return;
        }
        _ = StartCoreAsync(descriptor);
    }

    private async Task StartCoreAsync(TerminalLaunchDescriptor descriptor)
    {
        if (_control is null) return;
        try
        {
            StartError = null;
            await _session.StartAsync(descriptor, _control);
            IsRunning = true;
            HasExited = false;
            ExitCode = null;
        }
        catch (Exception ex)
        {
            IsRunning = false;
            HasExited = true;
            StartError = ex.Message;
        }
    }

    private void OnSessionProcessExited(object? sender, int exitCode)
    {
        IsRunning = false;
        HasExited = true;
        ExitCode = exitCode;
    }

    public void Kill() => _session.Kill();

    public void Dispose()
    {
        _session.ProcessExited -= OnSessionProcessExited;
        _session.Dispose();
    }
}
