using Hc900Crawler.Infrastructure.Docs;
using Hc900Crawler.Infrastructure.Kb;
using Microsoft.AspNetCore.Mvc;

namespace Hc900Crawler.Web.Controllers;

/// <summary>
/// 프로젝트 폴더 문서 탐색기 API. 조회는 공개, 변경 작업(편집/이름변경/삭제/폴더/업로드)은
/// KB admin 토큰(X-Kb-Token) 필요. 경로 안전·제외 규칙은 DocBrowserService 가 강제한다.
/// </summary>
[ApiController]
[Route("api/docs")]
public class DocsController : ControllerBase
{
    private readonly DocBrowserService _docs;
    private readonly IKbAuthService _auth;
    private readonly ILogger<DocsController> _logger;

    public DocsController(DocBrowserService docs, IKbAuthService auth, ILogger<DocsController> logger)
    {
        _docs = docs;
        _auth = auth;
        _logger = logger;
    }

    private Task<bool> IsAdminAsync(CancellationToken ct)
        => _auth.ValidateAsync(Request.Headers["X-Kb-Token"].ToString(), ct);

    private IActionResult Fail(string error) => Ok(new { success = false, error });

    // ── 메타/설정 ───────────────────────────────────────────────
    [HttpGet("config")]
    public async Task<IActionResult> Config(CancellationToken ct)
        => Ok(new
        {
            success = true,
            root = _docs.Root,
            canManage = await IsAdminAsync(ct),
            maxTextBytes = _docs.MaxTextBytes,
            maxUploadBytes = _docs.MaxUploadBytes,
        });

    // ── 디렉토리 목록 ───────────────────────────────────────────
    [HttpGet("tree")]
    public IActionResult Tree([FromQuery] string? path)
    {
        try
        {
            var entries = _docs.List(path).Select(e => new
            {
                name = e.Name,
                path = e.RelPath,
                type = e.IsDir ? "dir" : "file",
                size = e.Size,
                mtime = e.ModifiedUtc,
                ext = e.Ext,
            });
            return Ok(new { success = true, path = path ?? "", entries });
        }
        catch (DocBrowserException ex) { return Fail(ex.Message); }
    }

    // ── 텍스트 읽기 ─────────────────────────────────────────────
    [HttpGet("text")]
    public IActionResult Text([FromQuery] string path)
    {
        try
        {
            var r = _docs.ReadText(path);
            return Ok(new { success = true, text = r.Text, truncated = r.Truncated, size = r.Size, ext = r.Ext });
        }
        catch (DocBrowserException ex) { return Fail(ex.Message); }
    }

    // ── 원본 스트림 (pdf iframe / 다운로드) ─────────────────────
    [HttpGet("raw")]
    public IActionResult Raw([FromQuery] string path, [FromQuery] bool download = false)
    {
        try
        {
            var r = _docs.OpenRaw(path);
            if (download)
                return File(r.Stream, "application/octet-stream", r.FileName);
            return File(r.Stream, r.ContentType);   // inline (pdf 등)
        }
        catch (DocBrowserException ex) { return NotFound(new { success = false, error = ex.Message }); }
    }

    // ── 텍스트 저장 (admin) ─────────────────────────────────────
    public sealed record WriteRequest(string Path, string Content);

    [HttpPut("text")]
    [RequestSizeLimit(8_000_000)]
    public async Task<IActionResult> WriteText([FromBody] WriteRequest req, CancellationToken ct)
    {
        if (!await IsAdminAsync(ct)) return Unauthorized(new { success = false, error = "unauthorized" });
        if (req == null || string.IsNullOrEmpty(req.Path)) return Fail("path required");
        try
        {
            _docs.WriteText(req.Path, req.Content ?? "");
            _logger.LogInformation("[Docs] 저장 {Path}", req.Path);
            return Ok(new { success = true });
        }
        catch (DocBrowserException ex) { return Fail(ex.Message); }
    }

    // ── 이름변경/이동 (admin) ───────────────────────────────────
    public sealed record RenameRequest(string From, string To);

    [HttpPost("rename")]
    public async Task<IActionResult> Rename([FromBody] RenameRequest req, CancellationToken ct)
    {
        if (!await IsAdminAsync(ct)) return Unauthorized(new { success = false, error = "unauthorized" });
        if (req == null || string.IsNullOrEmpty(req.From) || string.IsNullOrEmpty(req.To))
            return Fail("from/to required");
        try
        {
            var newRel = _docs.Rename(req.From, req.To);
            _logger.LogInformation("[Docs] 이름변경 {From} → {To}", req.From, newRel);
            return Ok(new { success = true, path = newRel });
        }
        catch (DocBrowserException ex) { return Fail(ex.Message); }
    }

    // ── 새 폴더 (admin) ─────────────────────────────────────────
    public sealed record MkdirRequest(string Path);

    [HttpPost("mkdir")]
    public async Task<IActionResult> Mkdir([FromBody] MkdirRequest req, CancellationToken ct)
    {
        if (!await IsAdminAsync(ct)) return Unauthorized(new { success = false, error = "unauthorized" });
        if (req == null || string.IsNullOrEmpty(req.Path)) return Fail("path required");
        try
        {
            var rel = _docs.MakeDir(req.Path);
            return Ok(new { success = true, path = rel });
        }
        catch (DocBrowserException ex) { return Fail(ex.Message); }
    }

    // ── 삭제 (admin) ────────────────────────────────────────────
    [HttpDelete]
    public async Task<IActionResult> Delete([FromQuery] string path, [FromQuery] bool recursive, CancellationToken ct)
    {
        if (!await IsAdminAsync(ct)) return Unauthorized(new { success = false, error = "unauthorized" });
        if (string.IsNullOrEmpty(path)) return Fail("path required");
        try
        {
            _docs.Delete(path, recursive);
            _logger.LogInformation("[Docs] 삭제 {Path} (recursive={R})", path, recursive);
            return Ok(new { success = true });
        }
        catch (DocBrowserException ex) { return Fail(ex.Message); }
    }

    // ── 업로드 (admin) ──────────────────────────────────────────
    [HttpPost("upload")]
    [RequestSizeLimit(100_000_000)]
    public async Task<IActionResult> Upload([FromForm] IFormFile file, [FromForm] string? path, CancellationToken ct)
    {
        if (!await IsAdminAsync(ct)) return Unauthorized(new { success = false, error = "unauthorized" });
        if (file == null || file.Length == 0) return Fail("file required");
        try
        {
            await using var stream = file.OpenReadStream();
            var rel = await _docs.SaveUploadAsync(path, file.FileName, stream, ct);
            _logger.LogInformation("[Docs] 업로드 {Path} ({Size} bytes)", rel, file.Length);
            return Ok(new { success = true, path = rel });
        }
        catch (DocBrowserException ex) { return Fail(ex.Message); }
    }
}
