using AuthorBuddy.Web.Data;
using AuthorBuddy.Web.Models;
using LiteDB;

namespace AuthorBuddy.Web.Services;

public class ProjectService : IProjectService
{
    private readonly ILiteDatabase _db;
    private readonly IFileScannerService _fileScannerService;

    public ProjectService(ILiteDatabase db, IFileScannerService fileScannerService)
    {
        _db = db;
        _fileScannerService = fileScannerService;
    }

    private ILiteCollection<FileCategoryEntity> CategoryCol =>
        _db.GetCollection<FileCategoryEntity>("fileCategories");

    public Task<int> CreateNewProjectAsync(string projectName, string rootPath, int userId, ProjectTemplate template = ProjectTemplate.Novel)
    {
        if (template == ProjectTemplate.Technical)
        {
            string[] subFolders = { "00_Anforderungen", "01_UseCases", "02_Architektur", "03_Glossar" };
            foreach (var subFolder in subFolders)
            {
                Directory.CreateDirectory(Path.Combine(rootPath, subFolder));
            }

            string readmePath = Path.Combine(rootPath, "00_Projekt_Info.md");
            File.WriteAllText(readmePath, $"# Projekt: {projectName}\nErstellt am: {DateTime.Now}\nTyp: Technische Vorlage (Requirements)");

            string lastenheftPath = Path.Combine(rootPath, "Lastenheft.md");
            File.WriteAllText(lastenheftPath, $"# {projectName} – Lastenheft\n\n" +
                "## 1. Zielbestimmung\n\n" +
                "## 2. Produkteinsatz\n\n" +
                "## 3. Produktumgebung\n\n" +
                "## 4. Funktionale Anforderungen\n\n" +
                "## 5. Nicht-funktionale Anforderungen\n\n" +
                "## 6. Lieferumfang\n\n");

            string pflichtenheftPath = Path.Combine(rootPath, "Pflichtenheft.md");
            File.WriteAllText(pflichtenheftPath, $"# {projectName} – Pflichtenheft\n\n" +
                "## 1. Systemarchitektur\n\n" +
                "## 2. Komponentenbeschreibung\n\n" +
                "## 3. Schnittstellendefinition\n\n" +
                "## 4. Datenmodell\n\n" +
                "## 5. Technische Umsetzung\n\n");
        }
        else
        {
            string[] subFolders = { "00_Drafts", "01_Fakten", "02_Characters", "03_Archive" };
            foreach (var subFolder in subFolders)
            {
                Directory.CreateDirectory(Path.Combine(rootPath, subFolder));
            }

            string readmePath = Path.Combine(rootPath, "00_Projekt_Info.md");
            File.WriteAllText(readmePath, $"# Projekt: {projectName}\nErstellt am: {DateTime.Now}");

            string exposeePath = Path.Combine(rootPath, "Die Handlung (Das Exposé).md");
            File.WriteAllText(exposeePath, $"# {projectName} – Die Handlung (Das Exposé)\n\n" +
                "## Überblick\n\n" +
                "## Erster Akt (Aufbau)\n\n" +
                "## Zweiter Akt (Konfrontation)\n\n" +
                "## Dritter Akt (Auflösung)\n\n" +
                "## Wichtige Wendepunkte\n\n" +
                "## Charakterbögen\n\n");

            string ideasPath = Path.Combine(rootPath, "Ideen Sammlung.md");
            File.WriteAllText(ideasPath, $"# Ideen Sammlung – {projectName}\n\n");
        }

        var col = _db.GetCollection<ProjectEntity>("projects");
        var entity = new ProjectEntity
        {
            Name = projectName,
            RootPath = rootPath,
            UserId = userId,
            LastOpened = DateTime.Now,
            Template = template
        };

        col.Insert(entity);
        return Task.FromResult(entity.Id);
    }

    public Task<List<Project>> GetAllProjectsAsync(int? userId = null)
    {
        var col = _db.GetCollection<ProjectEntity>("projects");
        var query = userId == null
            ? col.FindAll()
            : col.Find(p => p.UserId == userId);
        var list = query
            .OrderByDescending(p => p.LastOpened)
            .Select(p => new Project
            {
                Id = p.Id,
                Name = p.Name,
                RootPath = p.RootPath,
                Template = p.Template
            })
            .ToList();
        return Task.FromResult(list);
    }

    public Task<Project?> GetProjectAsync(int projectId)
    {
        var col = _db.GetCollection<ProjectEntity>("projects");
        var entity = col.FindById(projectId);
        if (entity == null) return Task.FromResult<Project?>(null);
        return Task.FromResult<Project?>(new Project
        {
            Id = entity.Id,
            Name = entity.Name,
            RootPath = entity.RootPath,
            Template = entity.Template
        });
    }

    public async Task<List<FileTreeItem>> GetProjectFileTreeAsync(int projectId)
    {
        var col = _db.GetCollection<ProjectEntity>("projects");
        var entity = col.FindById(projectId);
        if (entity == null || !Directory.Exists(entity.RootPath))
            return new List<FileTreeItem>();

        var categories = await GetFileCategoriesAsync(projectId);
        var result = new List<FileTreeItem>();
        BuildTree(entity.RootPath, result, 0, categories);
        return result;
    }

    private static void BuildTree(string directory, List<FileTreeItem> items, int depth, Dictionary<string, FileCategory> categories)
    {
        if (depth > 4) return;

        var dirInfo = new DirectoryInfo(directory);
        if (!dirInfo.Exists) return;

        foreach (var dir in dirInfo.GetDirectories().OrderBy(d => d.Name))
        {
            var node = new FileTreeItem
            {
                Name = dir.Name,
                FullPath = dir.FullName,
                IsDirectory = true
            };
            BuildTree(dir.FullName, node.Children, depth + 1, categories);
            if (node.Children.Count > 0 || dir.Name.StartsWith("00_") || dir.Name.StartsWith("01_") || dir.Name.StartsWith("02_"))
                items.Add(node);
        }

        foreach (var file in dirInfo.GetFiles("*.md").OrderBy(f => f.Name))
        {
            categories.TryGetValue(file.FullName, out var cat);
            items.Add(new FileTreeItem
            {
                Name = file.Name,
                FullPath = file.FullName,
                IsDirectory = false,
                Category = cat
            });
        }
    }

    public async Task SyncProjectFilesAsync(int projectId, string directoryPath, IProgress<string> progress)
    {
        await _fileScannerService.ScanProjectFolderAsync(projectId, directoryPath, progress);
    }

    public Task<int> CopyProjectAsync(int sourceProjectId, string newName, string newRootPath, int userId)
    {
        var col = _db.GetCollection<ProjectEntity>("projects");
        var source = col.FindById(sourceProjectId);
        if (source == null)
            throw new InvalidOperationException("Source project not found.");

        Directory.CreateDirectory(newRootPath);
        CopyDirectory(source.RootPath, newRootPath);

        var entity = new ProjectEntity
        {
            Name = newName,
            RootPath = newRootPath,
            UserId = userId,
            LastOpened = DateTime.Now,
            Template = source.Template
        };
        col.Insert(entity);

        var settings = _db.GetCollection<SettingEntity>("settings");
        var projectSettings = settings.Find(s =>
            s.UserId == userId && s.ProjectId == sourceProjectId);
        foreach (var s in projectSettings)
        {
            settings.Insert(new SettingEntity
            {
                Key = s.Key,
                Value = s.Value,
                UserId = userId,
                ProjectId = entity.Id
            });
        }

        return Task.FromResult(entity.Id);
    }

    private static void CopyDirectory(string sourceDir, string destDir)
    {
        foreach (var dir in Directory.GetDirectories(sourceDir, "*", SearchOption.AllDirectories))
        {
            Directory.CreateDirectory(dir.Replace(sourceDir, destDir));
        }
        foreach (var file in Directory.GetFiles(sourceDir, "*.*", SearchOption.AllDirectories))
        {
            var dest = file.Replace(sourceDir, destDir);
            File.Copy(file, dest, true);
        }
    }

    public Task<Dictionary<string, FileCategory>> GetFileCategoriesAsync(int projectId)
    {
        var entries = CategoryCol.Find(e => e.ProjectId == projectId).ToList();
        var result = new Dictionary<string, FileCategory>(StringComparer.OrdinalIgnoreCase);
        foreach (var e in entries)
            result[e.FilePath] = e.Category;
        return Task.FromResult(result);
    }

    public Task<bool> SetFileCategoryAsync(int projectId, string filePath, FileCategory category)
    {
        if (!FileCategoryHelper.IsValid(category)) return Task.FromResult(false);

        if (category.HasFlag(FileCategory.Exposee))
        {
            var existingExposee = CategoryCol.Find(e => e.ProjectId == projectId)
                .FirstOrDefault(e => e.Category.HasFlag(FileCategory.Exposee) && e.FilePath != filePath);
            if (existingExposee != null) return Task.FromResult(false);
        }

        var col = CategoryCol;
        var existing = col.FindOne(e => e.ProjectId == projectId && e.FilePath == filePath);
        if (existing != null)
        {
            if (category == FileCategory.None)
                col.Delete(existing.Id);
            else
            {
                existing.Category = category;
                col.Update(existing);
            }
        }
        else if (category != FileCategory.None)
        {
            col.Insert(new FileCategoryEntity
            {
                ProjectId = projectId,
                FilePath = filePath,
                Category = category
            });
        }
        return Task.FromResult(true);
    }

    public Task ClearFileCategoryAsync(int projectId, string filePath)
    {
        var col = CategoryCol;
        var existing = col.FindOne(e => e.ProjectId == projectId && e.FilePath == filePath);
        if (existing != null)
            col.Delete(existing.Id);
        return Task.CompletedTask;
    }
}
