using System.Data;
using Dapper;
using PlotLine.Models;

namespace PlotLine.Data;

public interface IProjectBackupRepository
{
    Task<ProjectBackupExport?> GetProjectBackupAsync(int projectId, int? userId = null);
}

public sealed class ProjectBackupRepository(ISqlConnectionFactory connectionFactory) : IProjectBackupRepository
{
    public async Task<ProjectBackupExport?> GetProjectBackupAsync(int projectId, int? userId = null)
    {
        using var connection = connectionFactory.CreateConnection();
        using var result = await connection.QueryMultipleAsync(
            "dbo.ProjectBackup_Export",
            new { ProjectID = projectId, UserID = userId },
            commandType: CommandType.StoredProcedure,
            commandTimeout: 180);

        var project = await result.ReadSingleOrDefaultAsync<Project>();
        var data = new ProjectBackupData
        {
            Books = (await result.ReadAsync<Book>()).ToList(),
            Chapters = (await result.ReadAsync<Chapter>()).ToList(),
            Scenes = (await result.ReadAsync<Scene>()).ToList(),
            ScenePurposes = (await result.ReadAsync<ProjectBackupScenePurpose>()).ToList(),
            SceneOutcomes = (await result.ReadAsync<ProjectBackupSceneOutcome>()).ToList(),
            SceneMetricTypes = (await result.ReadAsync<SceneMetricType>()).ToList(),
            SceneMetricValues = (await result.ReadAsync<SceneMetricValue>()).ToList(),
            TimelineSettings = (await result.ReadAsync<ProjectTimelineSettings>()).ToList(),
            TimelineMetricSettings = (await result.ReadAsync<ProjectTimelineMetricSetting>()).ToList(),
            TimelineViewPresets = (await result.ReadAsync<TimelineViewPreset>()).ToList(),
            PlotLines = (await result.ReadAsync<PlotLineItem>()).ToList(),
            PlotThreads = (await result.ReadAsync<PlotThread>()).ToList(),
            ThreadEvents = (await result.ReadAsync<ThreadEvent>()).ToList(),
            StoryAssets = (await result.ReadAsync<StoryAsset>()).ToList(),
            AssetStates = (await result.ReadAsync<AssetState>()).ToList(),
            AssetEvents = (await result.ReadAsync<AssetEvent>()).ToList(),
            AssetDependencies = (await result.ReadAsync<AssetDependency>()).ToList(),
            AssetCustodyEvents = (await result.ReadAsync<AssetCustodyEvent>()).ToList(),
            AssetCustodyCharacters = (await result.ReadAsync<ProjectBackupAssetCustodyCharacter>()).ToList(),
            Characters = (await result.ReadAsync<Character>()).ToList(),
            SceneCharacters = (await result.ReadAsync<SceneCharacter>()).ToList(),
            CharacterAttributeTypes = (await result.ReadAsync<CharacterAttributeType>()).ToList(),
            CharacterAttributeEvents = (await result.ReadAsync<CharacterAttributeEvent>()).ToList(),
            CharacterKnowledge = (await result.ReadAsync<CharacterKnowledgeItem>()).ToList(),
            CharacterRelationships = (await result.ReadAsync<CharacterRelationship>()).ToList(),
            RelationshipEvents = (await result.ReadAsync<RelationshipEvent>()).ToList(),
            Locations = (await result.ReadAsync<LocationItem>()).ToList(),
            LocationRelationships = (await result.ReadAsync<LocationRelationship>()).ToList(),
            FloorPlans = (await result.ReadAsync<FloorPlan>()).ToList(),
            FloorPlanFloors = (await result.ReadAsync<FloorPlanFloor>()).ToList(),
            FloorPlanBlocks = (await result.ReadAsync<FloorPlanBlock>()).ToList(),
            FloorPlanTransitions = (await result.ReadAsync<FloorPlanTransition>()).ToList(),
            SceneAssetLocations = (await result.ReadAsync<SceneAssetLocation>()).ToList(),
            SceneFloorPlanOccupancy = (await result.ReadAsync<SceneFloorPlanOccupancy>()).ToList(),
            SceneDependencies = (await result.ReadAsync<SceneDependency>()).ToList(),
            ValidationRuns = (await result.ReadAsync<ProjectBackupValidationRun>()).ToList(),
            ContinuityWarnings = (await result.ReadAsync<ContinuityWarning>()).ToList(),
            SceneNotes = (await result.ReadAsync<SceneNote>()).ToList(),
            SceneWorkflow = (await result.ReadAsync<SceneWorkflow>()).ToList(),
            SceneChecklistItems = (await result.ReadAsync<SceneChecklistItem>()).ToList(),
            SceneAttachments = (await result.ReadAsync<SceneAttachment>()).ToList(),
            ChapterGoals = (await result.ReadAsync<ChapterGoal>()).ToList(),
            Scenarios = (await result.ReadAsync<Scenario>()).ToList(),
            ScenarioSceneOrder = (await result.ReadAsync<ProjectBackupScenarioSceneOrder>()).ToList(),
            ScenarioValidationRuns = (await result.ReadAsync<ProjectBackupScenarioValidationRun>()).ToList(),
            ScenarioWarnings = (await result.ReadAsync<ScenarioWarning>()).ToList(),
            GenreMetricPresets = (await result.ReadAsync<GenreMetricPreset>()).ToList(),
            GenreMetricPresetMetrics = (await result.ReadAsync<ProjectBackupGenreMetricPresetMetric>()).ToList(),
            ProjectAccess = (await result.ReadAsync<ProjectBackupAccessRecord>()).ToList(),
            UserFiles = (await result.ReadAsync<UserFile>()).ToList()
        };

        if (project is null)
        {
            return null;
        }

        var bookMetadata = (await connection.QueryAsync<Book>(
            "dbo.ProjectBackup_BookMetadata_List",
            new { ProjectID = projectId },
            commandType: CommandType.StoredProcedure)).ToDictionary(x => x.BookID);
        foreach (var book in data.Books)
        {
            if (!bookMetadata.TryGetValue(book.BookID, out var metadata))
            {
                continue;
            }

            book.Subtitle = metadata.Subtitle;
            book.Tagline = metadata.Tagline;
            book.ShortDescription = metadata.ShortDescription;
            book.Status = metadata.Status;
            book.TargetWordCount = metadata.TargetWordCount;
            book.WritingStartDate = metadata.WritingStartDate;
            book.TargetCompletionDate = metadata.TargetCompletionDate;
            book.PublicationDate = metadata.PublicationDate;
            book.CoverThumbnailPath = metadata.CoverThumbnailPath;
            book.CoverStandardPath = metadata.CoverStandardPath;
        }

        return new ProjectBackupExport
        {
            ExportedAtUtc = DateTime.UtcNow,
            Project = project,
            Data = data,
            Diagnostics = new ProjectBackupDiagnostics
            {
                ProjectId = projectId,
                IncludedEntitySets =
                [
                    nameof(data.Books),
                    nameof(data.Chapters),
                    nameof(data.Scenes),
                    nameof(data.ScenePurposes),
                    nameof(data.SceneOutcomes),
                    nameof(data.SceneMetricTypes),
                    nameof(data.SceneMetricValues),
                    nameof(data.TimelineSettings),
                    nameof(data.TimelineMetricSettings),
                    nameof(data.TimelineViewPresets),
                    nameof(data.PlotLines),
                    nameof(data.PlotThreads),
                    nameof(data.ThreadEvents),
                    nameof(data.StoryAssets),
                    nameof(data.AssetStates),
                    nameof(data.AssetEvents),
                    nameof(data.AssetDependencies),
                    nameof(data.AssetCustodyEvents),
                    nameof(data.AssetCustodyCharacters),
                    nameof(data.Characters),
                    nameof(data.SceneCharacters),
                    nameof(data.CharacterAttributeTypes),
                    nameof(data.CharacterAttributeEvents),
                    nameof(data.CharacterKnowledge),
                    nameof(data.CharacterRelationships),
                    nameof(data.RelationshipEvents),
                    nameof(data.Locations),
                    nameof(data.LocationRelationships),
                    nameof(data.FloorPlans),
                    nameof(data.FloorPlanFloors),
                    nameof(data.FloorPlanBlocks),
                    nameof(data.FloorPlanTransitions),
                    nameof(data.SceneAssetLocations),
                    nameof(data.SceneFloorPlanOccupancy),
                    nameof(data.SceneDependencies),
                    nameof(data.ValidationRuns),
                    nameof(data.ContinuityWarnings),
                    nameof(data.SceneNotes),
                    nameof(data.SceneWorkflow),
                    nameof(data.SceneChecklistItems),
                    nameof(data.SceneAttachments),
                    nameof(data.ChapterGoals),
                    nameof(data.Scenarios),
                    nameof(data.ScenarioSceneOrder),
                    nameof(data.ScenarioValidationRuns),
                    nameof(data.ScenarioWarnings),
                    nameof(data.GenreMetricPresets),
                    nameof(data.GenreMetricPresetMetrics),
                    nameof(data.ProjectAccess),
                    nameof(data.UserFiles)
                ],
                Note = "Original database IDs are included for diagnostics. Project access records are exported for reference only; importing a project backup creates a new project owned by the importing user and does not recreate collaborators."
            }
        };
    }
}
