using Microsoft.AspNetCore.Mvc;
using RoslynBridge.WebApi.Models;
using System.Text;
using System.Text.Json;
using System.Linq;
using RoslynBridge.WebApi.Services;

namespace RoslynBridge.WebApi.Controllers;

/// <summary>
/// Controller for Roslyn code analysis operations
/// </summary>
[ApiController]
[Route("api/[controller]")]
[Produces("application/json")]
public class RoslynController : ControllerBase
{
    private readonly IRoslynBridgeClient _bridgeClient;
    private readonly ILogger<RoslynController> _logger;

    public RoslynController(IRoslynBridgeClient bridgeClient, ILogger<RoslynController> logger)
    {
        _bridgeClient = bridgeClient;
        _logger = logger;
    }

    /// <summary>
    /// Execute a Roslyn query
    /// </summary>
    /// <param name="request">The query request</param>
    /// <param name="instancePort">Optional: specific VS instance port to target</param>
    /// <param name="cancellationToken">Cancellation token</param>
    /// <returns>The query result</returns>
    /// <response code="200">Query executed successfully</response>
    /// <response code="400">Invalid request</response>
    /// <response code="500">Internal server error</response>
    [HttpPost("query")]
    [ProducesResponseType(typeof(RoslynQueryResponse), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    [ProducesResponseType(StatusCodes.Status500InternalServerError)]
    public async Task<ActionResult<RoslynQueryResponse>> ExecuteQuery(
        [FromBody] RoslynQueryRequest request,
        [FromQuery] int? instancePort = null,
        CancellationToken cancellationToken = default)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        _logger.LogInformation("Received query request: {QueryType}", request.QueryType);

        var result = await _bridgeClient.ExecuteQueryAsync(request, instancePort, cancellationToken);

        if (!result.Success)
        {
            _logger.LogWarning("Query failed: {Error}", result.Error);
        }

        return Ok(result);
    }

    /// <summary>
    /// Get all projects in the solution
    /// </summary>
    /// <param name="instancePort">Optional: specific VS instance port to target</param>
    /// <param name="cancellationToken">Cancellation token</param>
    /// <returns>List of projects</returns>
    [HttpGet("projects")]
    [ProducesResponseType(typeof(RoslynQueryResponse), StatusCodes.Status200OK)]
    public async Task<ActionResult<RoslynQueryResponse>> GetProjects(
        [FromQuery] int? instancePort = null,
        CancellationToken cancellationToken = default)
    {
        var request = new RoslynQueryRequest { QueryType = "getprojects" };
        var result = await _bridgeClient.ExecuteQueryAsync(request, instancePort, cancellationToken);
        return Ok(result);
    }

    /// <summary>
    /// Get solution overview
    /// </summary>
    /// <param name="instancePort">Optional: specific VS instance port to target</param>
    /// <param name="cancellationToken">Cancellation token</param>
    /// <returns>Solution statistics and overview</returns>
    [HttpGet("solution/overview")]
    [ProducesResponseType(typeof(RoslynQueryResponse), StatusCodes.Status200OK)]
    public async Task<ActionResult<RoslynQueryResponse>> GetSolutionOverview(
        [FromQuery] int? instancePort = null,
        CancellationToken cancellationToken = default)
    {
        var request = new RoslynQueryRequest { QueryType = "getsolutionoverview" };
        var result = await _bridgeClient.ExecuteQueryAsync(request, instancePort, cancellationToken);
        return Ok(result);
    }

    /// <summary>
    /// Get a compact summary of the solution overview
    /// </summary>
    /// <param name="topNProjects">How many projects to list (by file count)</param>
    /// <param name="includeNamespaces">Include top-level namespaces in summary</param>
    /// <param name="format">One of: json | text | yaml</param>
    /// <param name="instancePort">Optional: specific VS instance port to target</param>
    /// <param name="cancellationToken">Cancellation token</param>
    /// <returns>Compact summary as JSON or plain text</returns>
    [HttpGet("solution/overview/summary")]
    [ProducesResponseType(typeof(RoslynQueryResponse), StatusCodes.Status200OK)]
    public async Task<IActionResult> GetSolutionOverviewSummary(
        [FromQuery] int topNProjects = 5,
        [FromQuery] bool includeNamespaces = true,
        [FromQuery] string? format = "json",
        [FromQuery] int? instancePort = null,
        CancellationToken cancellationToken = default)
    {
        var request = new RoslynQueryRequest { QueryType = "getsolutionoverview" };
        var result = await _bridgeClient.ExecuteQueryAsync(request, instancePort, cancellationToken);

        if (!result.Success)
        {
            if (string.Equals(format, "json", StringComparison.OrdinalIgnoreCase))
            {
                return Ok(result);
            }
            var err = result.Error ?? "Unknown error";
            return Content($"error: {err}", "text/plain", Encoding.UTF8);
        }

        // Expect Data as JsonElement; shape per Models.SolutionOverview
        int projectCount = 0;
        int documentCount = 0;
        var projectsSummary = new List<(string Name, int FileCount)>();
        var topLevelNamespaces = new List<string>();

        if (result.Data is JsonElement dataEl && dataEl.ValueKind == JsonValueKind.Object)
        {
            if (dataEl.TryGetProperty("projectCount", out var pc)) projectCount = pc.GetInt32();
            if (dataEl.TryGetProperty("documentCount", out var dc)) documentCount = dc.GetInt32();
            if (includeNamespaces && dataEl.TryGetProperty("topLevelNamespaces", out var nsEl) && nsEl.ValueKind == JsonValueKind.Array)
            {
                foreach (var ns in nsEl.EnumerateArray())
                {
                    if (ns.ValueKind == JsonValueKind.String)
                    {
                        topLevelNamespaces.Add(ns.GetString() ?? string.Empty);
                    }
                }
            }
            if (dataEl.TryGetProperty("projects", out var prEl) && prEl.ValueKind == JsonValueKind.Array)
            {
                foreach (var p in prEl.EnumerateArray())
                {
                    string name = p.TryGetProperty("name", out var nEl) && nEl.ValueKind == JsonValueKind.String ? (nEl.GetString() ?? string.Empty) : string.Empty;
                    int files = p.TryGetProperty("fileCount", out var fEl) && fEl.ValueKind == JsonValueKind.Number ? fEl.GetInt32() : 0;
                    projectsSummary.Add((name, files));
                }
            }
        }

        var topProjects = projectsSummary
            .OrderByDescending(p => p.FileCount)
            .ThenBy(p => p.Name)
            .Take(Math.Max(0, topNProjects))
            .ToList();

        if (string.Equals(format, "json", StringComparison.OrdinalIgnoreCase))
        {
            var compact = new
            {
                projectCount,
                documentCount,
                projects = topProjects.Select(p => new { name = p.Name, files = p.FileCount }).ToList(),
                topLevelNamespaces = includeNamespaces ? topLevelNamespaces : null
            };

            return Ok(new RoslynQueryResponse
            {
                Success = true,
                Message = "Compact solution overview",
                Data = compact,
                Error = null
            });
        }

        // text or yaml: render a compact string
        var sb = new StringBuilder();
        if (string.Equals(format, "yaml", StringComparison.OrdinalIgnoreCase))
        {
            sb.AppendLine($"projectCount: {projectCount}");
            sb.AppendLine($"documentCount: {documentCount}");
            if (includeNamespaces)
            {
                sb.Append("topLevelNamespaces: [");
                sb.Append(string.Join(", ", topLevelNamespaces));
                sb.AppendLine("]");
            }
            sb.AppendLine("projects:");
            foreach (var p in topProjects)
            {
                sb.AppendLine($"  - name: {p.Name}");
                sb.AppendLine($"    files: {p.FileCount}");
            }
        }
        else
        {
            // text
            sb.AppendLine($"Projects: {projectCount} | Documents: {documentCount}");
            if (includeNamespaces && topLevelNamespaces.Count > 0)
            {
                sb.AppendLine($"TopNamespaces: {string.Join(", ", topLevelNamespaces)}");
            }
            sb.AppendLine("Top Projects:");
            foreach (var p in topProjects)
            {
                sb.AppendLine($" - {p.Name} ({p.FileCount})");
            }
        }

        return Content(sb.ToString(), "text/plain", Encoding.UTF8);
    }

    /// <summary>
    /// Get diagnostics (errors and warnings)
    /// </summary>
    /// <param name="filePath">Optional file path to filter diagnostics</param>
    /// <param name="instancePort">Optional: specific VS instance port to target</param>
    /// <param name="cancellationToken">Cancellation token</param>
    /// <returns>List of diagnostics</returns>
    [HttpGet("diagnostics")]
    [ProducesResponseType(typeof(RoslynQueryResponse), StatusCodes.Status200OK)]
    public async Task<ActionResult<RoslynQueryResponse>> GetDiagnostics(
        [FromQuery] string? filePath = null,
        [FromQuery] int? instancePort = null,
        CancellationToken cancellationToken = default)
    {
        var request = new RoslynQueryRequest
        {
            QueryType = "getdiagnostics",
            FilePath = filePath
        };
        var result = await _bridgeClient.ExecuteQueryAsync(request, instancePort, cancellationToken);
        return Ok(result);
    }

    /// <summary>
    /// Get symbol information at a specific position
    /// </summary>
    /// <param name="filePath">File path</param>
    /// <param name="line">Line number (1-based)</param>
    /// <param name="column">Column number (0-based)</param>
    /// <param name="instancePort">Optional: specific VS instance port to target</param>
    /// <param name="cancellationToken">Cancellation token</param>
    /// <returns>Symbol information</returns>
    [HttpGet("symbol")]
    [ProducesResponseType(typeof(RoslynQueryResponse), StatusCodes.Status200OK)]
    public async Task<ActionResult<RoslynQueryResponse>> GetSymbol(
        [FromQuery] string filePath,
        [FromQuery] int line,
        [FromQuery] int column,
        [FromQuery] int? instancePort = null,
        CancellationToken cancellationToken = default)
    {
        var request = new RoslynQueryRequest
        {
            QueryType = "getsymbol",
            FilePath = filePath,
            Line = line,
            Column = column
        };
        var result = await _bridgeClient.ExecuteQueryAsync(request, instancePort, cancellationToken);
        return Ok(result);
    }

    /// <summary>
    /// Find all references to a symbol
    /// </summary>
    /// <param name="filePath">File path</param>
    /// <param name="line">Line number (1-based)</param>
    /// <param name="column">Column number (0-based)</param>
    /// <param name="instancePort">Optional: specific VS instance port to target</param>
    /// <param name="cancellationToken">Cancellation token</param>
    /// <returns>List of references</returns>
    [HttpGet("references")]
    [ProducesResponseType(typeof(RoslynQueryResponse), StatusCodes.Status200OK)]
    public async Task<ActionResult<RoslynQueryResponse>> FindReferences(
        [FromQuery] string filePath,
        [FromQuery] int line,
        [FromQuery] int column,
        [FromQuery] int? instancePort = null,
        CancellationToken cancellationToken = default)
    {
        var request = new RoslynQueryRequest
        {
            QueryType = "findreferences",
            FilePath = filePath,
            Line = line,
            Column = column
        };
        var result = await _bridgeClient.ExecuteQueryAsync(request, instancePort, cancellationToken);
        return Ok(result);
    }

    /// <summary>
    /// Search for symbols by name
    /// </summary>
    /// <param name="symbolName">Symbol name or pattern</param>
    /// <param name="kind">Optional symbol kind filter</param>
    /// <param name="instancePort">Optional: specific VS instance port to target</param>
    /// <param name="cancellationToken">Cancellation token</param>
    /// <returns>List of matching symbols</returns>
    [HttpGet("symbol/search")]
    [ProducesResponseType(typeof(RoslynQueryResponse), StatusCodes.Status200OK)]
    public async Task<ActionResult<RoslynQueryResponse>> FindSymbol(
        [FromQuery] string symbolName,
        [FromQuery] string? kind = null,
        [FromQuery] int? instancePort = null,
        CancellationToken cancellationToken = default)
    {
        var request = new RoslynQueryRequest
        {
            QueryType = "findsymbol",
            SymbolName = symbolName
        };

        if (!string.IsNullOrEmpty(kind))
        {
            request.Parameters = new Dictionary<string, string> { ["kind"] = kind };
        }

        var result = await _bridgeClient.ExecuteQueryAsync(request, instancePort, cancellationToken);
        return Ok(result);
    }

    /// <summary>
    /// Format a document
    /// </summary>
    /// <param name="filePath">File path to format</param>
    /// <param name="instancePort">Optional: specific VS instance port to target</param>
    /// <param name="cancellationToken">Cancellation token</param>
    /// <returns>Format operation result</returns>
    [HttpPost("format")]
    [ProducesResponseType(typeof(RoslynQueryResponse), StatusCodes.Status200OK)]
    public async Task<ActionResult<RoslynQueryResponse>> FormatDocument(
        [FromBody] string filePath,
        [FromQuery] int? instancePort = null,
        CancellationToken cancellationToken = default)
    {
        var request = new RoslynQueryRequest
        {
            QueryType = "formatdocument",
            FilePath = filePath
        };
        var result = await _bridgeClient.ExecuteQueryAsync(request, instancePort, cancellationToken);
        return Ok(result);
    }

    /// <summary>
    /// Add a NuGet package to a project
    /// </summary>
    /// <param name="projectName">Project name</param>
    /// <param name="packageName">NuGet package name</param>
    /// <param name="version">Optional package version</param>
    /// <param name="instancePort">Optional: specific VS instance port to target</param>
    /// <param name="cancellationToken">Cancellation token</param>
    /// <returns>Operation result</returns>
    [HttpPost("project/package/add")]
    [ProducesResponseType(typeof(RoslynQueryResponse), StatusCodes.Status200OK)]
    public async Task<ActionResult<RoslynQueryResponse>> AddNuGetPackage(
        [FromQuery] string projectName,
        [FromQuery] string packageName,
        [FromQuery] string? version = null,
        [FromQuery] int? instancePort = null,
        CancellationToken cancellationToken = default)
    {
        var request = new RoslynQueryRequest
        {
            QueryType = "addnugetpackage",
            ProjectName = projectName,
            PackageName = packageName,
            Version = version
        };
        var result = await _bridgeClient.ExecuteQueryAsync(request, instancePort, cancellationToken);
        return Ok(result);
    }

    /// <summary>
    /// Build a project
    /// </summary>
    /// <param name="projectName">Project name</param>
    /// <param name="configuration">Build configuration (Debug/Release)</param>
    /// <param name="instancePort">Optional: specific VS instance port to target</param>
    /// <param name="cancellationToken">Cancellation token</param>
    /// <returns>Build result</returns>
    [HttpPost("project/build")]
    [ProducesResponseType(typeof(RoslynQueryResponse), StatusCodes.Status200OK)]
    public async Task<ActionResult<RoslynQueryResponse>> BuildProject(
        [FromQuery] string projectName,
        [FromQuery] string? configuration = null,
        [FromQuery] int? instancePort = null,
        CancellationToken cancellationToken = default)
    {
        var request = new RoslynQueryRequest
        {
            QueryType = "buildproject",
            ProjectName = projectName,
            Configuration = configuration
        };
        var result = await _bridgeClient.ExecuteQueryAsync(request, instancePort, cancellationToken);
        return Ok(result);
    }
}
