using System;
using AvaloniaEdit.Document;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using MarkdownEditor.Constants;
using MarkdownEditor.Models;
using MarkdownEditor.Services;

namespace MarkdownEditor.ViewModels;

/// <summary>
/// View model for a single markdown document tab.
/// Manages document content, editing state, and formatting operations.
/// </summary>
public partial class MarkdownTabViewModel : ViewModelBase
{
    private readonly IFileService _fileService;
    private readonly IMarkdownFormattingService _formattingService;

    [ObservableProperty]
    private string _title = AppConstants.Messages.DefaultTabTitle;

    private readonly TextDocument _document;

    [ObservableProperty]
    private bool _isPrettyView = false;

    [ObservableProperty]
    private string? _filePath;

    [ObservableProperty]
    private bool _isDirty = false;

    /// <summary>
    /// Gets the text selection used for formatting operations.
    /// </summary>
    public TextSelection Selection { get; } = new();

    /// <summary>
    /// Initializes a new instance of the <see cref="MarkdownTabViewModel"/> class.
    /// </summary>
    /// <param name="fileService">Service for file operations. If null, uses default implementation.</param>
    /// <param name="formattingService">Service for markdown formatting. If null, uses default implementation.</param>
    public MarkdownTabViewModel(IFileService? fileService = null, IMarkdownFormattingService? formattingService = null)
    {
        _fileService = fileService ?? new FileService();
        _formattingService = formattingService ?? new MarkdownFormattingService();

        _document = new TextDocument(AppConstants.Messages.DefaultEditorContent);
        _document.TextChanged += (s, e) =>
        {
            IsDirty = true;
            OnPropertyChanged(nameof(SafeMarkdownContent));
        };
    }

    /// <summary>
    /// Gets the underlying text document.
    /// </summary>
    public TextDocument Document => _document;

    /// <summary>
    /// Gets or sets the markdown content of the document.
    /// </summary>
    public string MarkdownContent
    {
        get => _document.Text;
        set
        {
            if (_document.Text != value)
            {
                _document.Text = value;
                OnPropertyChanged(nameof(SafeMarkdownContent));
            }
        }
    }

    /// <summary>
    /// Gets the markdown content safely, with error handling.
    /// </summary>
    public string SafeMarkdownContent
    {
        get
        {
            try
            {
                var content = _document.Text;
                if (string.IsNullOrWhiteSpace(content))
                    return string.Empty;

                return content;
            }
            catch
            {
                return AppConstants.Messages.ErrorRenderingMarkdown;
            }
        }
    }

    /// <summary>
    /// Gets the view mode text for display.
    /// </summary>
    public string ViewModeText => IsPrettyView ? "Pretty" : "Raw";

    /// <summary>
    /// Gets the display title with dirty indicator.
    /// </summary>
    public string DisplayTitle => IsDirty ? $"{Title} *" : Title;

    partial void OnTitleChanged(string value)
    {
        OnPropertyChanged(nameof(DisplayTitle));
    }

    partial void OnIsDirtyChanged(bool value)
    {
        OnPropertyChanged(nameof(DisplayTitle));
    }

    /// <summary>
    /// Loads a markdown file from disk.
    /// </summary>
    /// <param name="path">The file path to load.</param>
    public async void LoadFromFile(string path)
    {
        try
        {
            FilePath = path;
            var content = await _fileService.ReadFileAsync(path);
            _document.Text = content;
            Title = _fileService.GetFileName(path);
            IsDirty = false;
            OnPropertyChanged(nameof(SafeMarkdownContent));
        }
        catch
        {
            // Error loading file - fail silently
        }
    }

    /// <summary>
    /// Saves the document to disk.
    /// </summary>
    /// <param name="path">The file path to save to.</param>
    public async void SaveToFile(string path)
    {
        try
        {
            FilePath = path;
            await _fileService.WriteFileAsync(path, _document.Text);
            Title = _fileService.GetFileName(path);
            IsDirty = false;
        }
        catch
        {
            // Error saving file - fail silently
        }
    }

    // Formatting commands using the formatting service

    [RelayCommand]
    private void FormatBold()
    {
        Selection.CopyFrom(_formattingService.WrapSelection(
            _document, Selection, AppConstants.Markdown.Bold, AppConstants.Markdown.Bold));
    }

    [RelayCommand]
    private void FormatItalic()
    {
        Selection.CopyFrom(_formattingService.WrapSelection(
            _document, Selection, AppConstants.Markdown.Italic, AppConstants.Markdown.Italic));
    }

    [RelayCommand]
    private void FormatHeading1()
    {
        Selection.CopyFrom(_formattingService.AddLinePrefix(
            _document, Selection, AppConstants.Markdown.Heading1Prefix));
    }

    [RelayCommand]
    private void FormatHeading2()
    {
        Selection.CopyFrom(_formattingService.AddLinePrefix(
            _document, Selection, AppConstants.Markdown.Heading2Prefix));
    }

    [RelayCommand]
    private void FormatHeading3()
    {
        Selection.CopyFrom(_formattingService.AddLinePrefix(
            _document, Selection, AppConstants.Markdown.Heading3Prefix));
    }

    [RelayCommand]
    private void FormatBulletList()
    {
        Selection.CopyFrom(_formattingService.AddLinePrefix(
            _document, Selection, AppConstants.Markdown.BulletPrefix));
    }

    [RelayCommand]
    private void FormatNumberedList()
    {
        Selection.CopyFrom(_formattingService.AddLinePrefix(
            _document, Selection, AppConstants.Markdown.NumberedPrefix));
    }

    [RelayCommand]
    private void FormatCode()
    {
        Selection.CopyFrom(_formattingService.WrapSelection(
            _document, Selection, AppConstants.Markdown.InlineCode, AppConstants.Markdown.InlineCode));
    }

    [RelayCommand]
    private void FormatCodeBlock()
    {
        Selection.CopyFrom(_formattingService.WrapSelection(
            _document, Selection, AppConstants.Markdown.CodeBlockStart, AppConstants.Markdown.CodeBlockEnd));
    }

    [RelayCommand]
    private void FormatQuote()
    {
        Selection.CopyFrom(_formattingService.AddLinePrefix(
            _document, Selection, AppConstants.Markdown.QuotePrefix));
    }

    [RelayCommand]
    private void SetRawView()
    {
        IsPrettyView = false;
    }

    [RelayCommand]
    private void SetPrettyView()
    {
        IsPrettyView = true;
    }

    /// <summary>
    /// Inserts a hyperlink at the current selection.
    /// </summary>
    /// <param name="url">The URL to link to.</param>
    public void InsertHyperlink(string url)
    {
        var selectedText = _document.GetText(Selection.Start, Selection.End - Selection.Start);
        var linkText = string.IsNullOrWhiteSpace(selectedText) ? "link text" : selectedText;
        var markdown = $"[{linkText}]({url})";

        _document.Replace(Selection.Start, Selection.End - Selection.Start, markdown);

        // Update selection to highlight the link text for easy editing
        Selection.Start = Selection.Start;
        Selection.End = Selection.Start + linkText.Length + 2; // +2 for the brackets
    }

    /// <summary>
    /// Inserts an image at the current selection.
    /// </summary>
    /// <param name="url">The image URL.</param>
    public void InsertImage(string url)
    {
        var selectedText = _document.GetText(Selection.Start, Selection.End - Selection.Start);
        var altText = string.IsNullOrWhiteSpace(selectedText) ? "image" : selectedText;
        var markdown = $"![{altText}]({url})";

        _document.Replace(Selection.Start, Selection.End - Selection.Start, markdown);

        // Update selection to highlight the alt text for easy editing
        Selection.Start = Selection.Start + 2; // Skip "!["
        Selection.End = Selection.Start + altText.Length;
    }

    // Legacy properties for backward compatibility
    /// <summary>
    /// Gets or sets the selection start position.
    /// </summary>
    public int SelectionStart
    {
        get => Selection.Start;
        set => Selection.Start = value;
    }

    /// <summary>
    /// Gets or sets the selection end position.
    /// </summary>
    public int SelectionEnd
    {
        get => Selection.End;
        set => Selection.End = value;
    }
}
