using FastBlog.Core.Abstractions.Repositories.Blogs;
using FastBlog.Core.Models;
using FastBlog.Core.Models.Blogs;

namespace FastBlog.Core.Services;

public sealed class BlogService(IBlogMetaRepository metaRepository, IBlogFileRepository blogFileRepository)
{
    public async Task<Blog?> Get(string? slug)
    {
        slug = slug?.ToLower();
        var metaResult = await metaRepository.Get(slug);
        if (metaResult is null)
            return null;
        var sourceResult = await blogFileRepository.Read(metaResult.SourceLocation);

        if (sourceResult is null)
        {
            throw new FileNotFoundException("File with metadata cannot be found");
        }

        return new Blog
        {
            Metadata = metaResult,
            Text = sourceResult
        };
    }
    
    public async Task<Blog?> GetForEdit(int id)
    {
        var metaResult = await metaRepository.GetForEdit(id);
        
        if (metaResult is null)
            return null;
        
        var sourceResult = await blogFileRepository.Read(metaResult.SourceLocation);

        if (sourceResult is null)
            throw new FileNotFoundException("File with metadata cannot be found");

        return new Blog
        {
            Metadata = metaResult,
            Text = sourceResult
        };
    }


    public Task<Result<Blog, BusinessError>> Update(Blog blog)
    {
        blog.Metadata.Slug = blog.Metadata.Slug?.ToLower();
        return blog.Metadata.Id is null ? Create(blog) : UpdateInternal(blog);
    }
    
    public async Task<bool> Delete(int id)
    {
        var meta = await metaRepository.GetForEdit(id);
        if (meta is null)
            return false;
        
        var toDelete = meta.SourceLocation;
        
        var result = await metaRepository.Delete(id);
        if (!result)
            return false;

        return await blogFileRepository.Delete(toDelete);
    }
    
    private async Task<Result<Blog, BusinessError>> Create(Blog newBlog)
    {
        var metaResult = await metaRepository.Add(newBlog.Metadata);
        if (metaResult.IsError)
            return metaResult.AsError;

        var sourceResult = await blogFileRepository.Write(metaResult.AsOk.SourceLocation, newBlog.Text);

        if (sourceResult.IsError)
            return sourceResult.AsError;

        return new Blog
        {
            Metadata = metaResult.AsOk,
            Text = newBlog.Text
        };
    }


    private async Task<Result<Blog, BusinessError>> UpdateInternal(Blog blog)
    {
        var oldMeta = await metaRepository.GetForEdit(blog.Metadata.Id!.Value);
        if (oldMeta is null)
            return new BusinessError("not_found", "Blog not found");
        
        var toDelete = oldMeta.SourceLocation;
        
        var metaResult = await metaRepository.Update(blog.Metadata);
        if (metaResult.IsError)
            return metaResult.AsError;

        _ = await blogFileRepository.Delete(toDelete);
        
        var sourceResult = await blogFileRepository.Write(metaResult.AsOk.SourceLocation, blog.Text);

        if (sourceResult.IsError)
            return sourceResult.AsError;

        return new Blog
        {
            Metadata = metaResult.AsOk,
            Text = blog.Text
        };
    }
}