using Media.JoshHeaps.Net.Services;
using Microsoft.AspNetCore.Mvc;
using System.Security.Claims;

namespace Media.JoshHeaps.Net.Api;

[ApiController]
[Route("api/blog")]
public class BlogApi(BlogService blogService, DbExecutor dbExecutor, IHttpClientFactory httpClientFactory, IConfiguration configuration, ILogger<BlogApi> logger) : ControllerBase
{
    [HttpGet("posts")]
    public async Task<IActionResult> GetPosts()
    {
        var posts = await blogService.GetAllPostsAsync();
        return Ok(posts.Select(MapToPublicDto));
    }

    [HttpGet("posts/{slug}")]
    public async Task<IActionResult> GetPostBySlug(string slug)
    {
        var post = await blogService.GetPostBySlugAsync(slug);
        if (post is null) return NotFound();
        return Ok(MapToPublicDto(post));
    }

    [HttpGet("posts/tags/{tag}")]
    public async Task<IActionResult> GetPostsByTag(string tag)
    {
        var posts = await blogService.GetPostsByTagAsync(tag);
        return Ok(posts.Select(MapToPublicDto));
    }

    [HttpGet("posts/admin/{id}")]
    public async Task<IActionResult> GetPostForAdmin(long id)
    {
        var userId = GetUserIdFromAuth();
        if (userId is null) return Unauthorized();
        if (!await IsAdmin(userId.Value)) return Forbid();

        var post = await blogService.GetPostByIdAsync(id);
        if (post is null) return NotFound();
        return Ok(MapToAdminDto(post));
    }

    [HttpPost("posts")]
    public async Task<IActionResult> CreatePost([FromBody] CreateBlogPostRequest request)
    {
        var userId = GetUserIdFromAuth();
        if (userId is null) return Unauthorized();
        if (!await IsAdmin(userId.Value)) return Forbid();

        if (string.IsNullOrWhiteSpace(request.Title))
            return BadRequest(new { error = "Title is required" });
        if (string.IsNullOrWhiteSpace(request.MarkdownContent))
            return BadRequest(new { error = "Content is required" });

        var post = await blogService.CreatePostAsync(
            request.Title.Trim(),
            request.Summary?.Trim() ?? "",
            request.MarkdownContent,
            request.Tags ?? [],
            userId.Value,
            request.PublishedDate ?? DateTime.UtcNow);

        if (post is null)
            return StatusCode(500, new { error = "Failed to create post" });

        _ = InvalidateFrontendCacheAsync();
        return Ok(MapToAdminDto(post));
    }

    [HttpPut("posts/{id}")]
    public async Task<IActionResult> UpdatePost(long id, [FromBody] UpdateBlogPostRequest request)
    {
        var userId = GetUserIdFromAuth();
        if (userId is null) return Unauthorized();
        if (!await IsAdmin(userId.Value)) return Forbid();

        if (string.IsNullOrWhiteSpace(request.Title))
            return BadRequest(new { error = "Title is required" });
        if (string.IsNullOrWhiteSpace(request.MarkdownContent))
            return BadRequest(new { error = "Content is required" });

        var post = await blogService.UpdatePostAsync(
            id,
            request.Title.Trim(),
            request.Summary?.Trim() ?? "",
            request.MarkdownContent,
            request.Tags ?? [],
            request.PublishedDate ?? DateTime.UtcNow);

        if (post is null)
            return NotFound(new { error = "Post not found" });

        _ = InvalidateFrontendCacheAsync();
        return Ok(MapToAdminDto(post));
    }

    [HttpDelete("posts/{id}")]
    public async Task<IActionResult> DeletePost(long id)
    {
        var userId = GetUserIdFromAuth();
        if (userId is null) return Unauthorized();
        if (!await IsAdmin(userId.Value)) return Forbid();

        var deleted = await blogService.DeletePostAsync(id);
        if (!deleted) return NotFound(new { error = "Post not found" });

        _ = InvalidateFrontendCacheAsync();
        return Ok(new { success = true });
    }

    [HttpPost("images")]
    public async Task<IActionResult> UploadImage(IFormFile file)
    {
        var userId = GetUserIdFromAuth();
        if (userId is null) return Unauthorized();
        if (!await IsAdmin(userId.Value)) return Forbid();

        if (file is null || file.Length == 0)
            return BadRequest(new { error = "No file provided" });

        var (url, error) = await blogService.SaveImageAsync(file);
        if (url is null)
            return BadRequest(new { error });

        return Ok(new { url });
    }

    [HttpGet("images/{fileName}")]
    public IActionResult GetImage(string fileName)
    {
        var (filePath, mimeType) = blogService.GetImagePath(fileName);
        if (filePath is null)
            return NotFound();

        return PhysicalFile(filePath, mimeType!);
    }

    private static object MapToPublicDto(Models.BlogPost post) => new
    {
        post.Id,
        post.Slug,
        post.Title,
        post.Summary,
        post.Tags,
        post.PublishedDate,
        post.HtmlContent
    };

    private static object MapToAdminDto(Models.BlogPost post) => new
    {
        post.Id,
        post.Slug,
        post.Title,
        post.Summary,
        post.MarkdownContent,
        post.Tags,
        post.PublishedDate,
        post.CreatedAt,
        post.UpdatedAt
    };

    private async Task InvalidateFrontendCacheAsync()
    {
        try
        {
            var url = configuration["BlogCache:InvalidateUrl"];
            var key = configuration["BlogCache:InvalidateKey"];
            if (string.IsNullOrEmpty(url) || string.IsNullOrEmpty(key)) return;

            var client = httpClientFactory.CreateClient();
            client.Timeout = TimeSpan.FromSeconds(5);
            var request = new HttpRequestMessage(HttpMethod.Post, url);
            request.Headers.Add("X-Invalidate-Key", key);
            await client.SendAsync(request);
        }
        catch (Exception ex)
        {
            logger.LogWarning(ex, "Failed to invalidate frontend blog cache");
        }
    }

    private async Task<bool> IsAdmin(long userId)
    {
        return await dbExecutor.ExecuteAsync<bool>(
            "SELECT EXISTS(SELECT 1 FROM app.user_roles ur JOIN app.roles r ON ur.role_id = r.id WHERE ur.user_id = @UserId AND r.name = 'admin')",
            new { UserId = userId });
    }

    private long? GetUserIdFromAuth()
    {
        var userIdClaim = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
        if (!string.IsNullOrEmpty(userIdClaim) && long.TryParse(userIdClaim, out var jwtUserId))
            return jwtUserId;

        var userIdString = HttpContext.Session.GetString("UserId");
        if (!string.IsNullOrEmpty(userIdString) && long.TryParse(userIdString, out var sessionUserId))
            return sessionUserId;

        return null;
    }
}

public record CreateBlogPostRequest(string Title, string? Summary, string MarkdownContent, List<string>? Tags, DateTime? PublishedDate);
public record UpdateBlogPostRequest(string Title, string? Summary, string MarkdownContent, List<string>? Tags, DateTime? PublishedDate);
