import CoreData
@testable import PortfolioJournal

/// Error types for TestCoreDataStack
enum TestCoreDataStackError: Error {
    case modelNotFound
    case storeLoadFailed(Error)
}

/// In-memory Core Data stack for testing purposes
/// This allows tests to run in isolation without affecting the actual database
final class TestCoreDataStack {

    let persistentContainer: NSPersistentContainer

    var viewContext: NSManagedObjectContext {
        persistentContainer.viewContext
    }

    init() throws {
        // Use the same approach as NSPersistentContainer(name:) which auto-finds the model
        // When tests run with the app as TEST_HOST, Bundle.main is the app bundle
        let modelName = "PortfolioJournal"

        // Create the container - it will find the model in the main bundle
        persistentContainer = NSPersistentContainer(name: modelName)

        // Configure for in-memory storage
        let description = NSPersistentStoreDescription()
        description.type = NSInMemoryStoreType
        description.shouldAddStoreAsynchronously = false

        persistentContainer.persistentStoreDescriptions = [description]

        var loadError: Error?
        persistentContainer.loadPersistentStores { _, error in
            loadError = error
        }

        if let error = loadError {
            throw TestCoreDataStackError.storeLoadFailed(error)
        }

        viewContext.automaticallyMergesChangesFromParent = true
        viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
    }

    /// Creates a fresh context for each test
    func newBackgroundContext() -> NSManagedObjectContext {
        let context = persistentContainer.newBackgroundContext()
        context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
        return context
    }

    /// Resets the in-memory store (call in tearDown)
    func reset() {
        let fetchRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest(entityName: "Snapshot")
        let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)
        try? viewContext.execute(deleteRequest)

        let sourceFetch: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest(entityName: "InvestmentSource")
        let sourceDelete = NSBatchDeleteRequest(fetchRequest: sourceFetch)
        try? viewContext.execute(sourceDelete)

        let categoryFetch: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest(entityName: "Category")
        let categoryDelete = NSBatchDeleteRequest(fetchRequest: categoryFetch)
        try? viewContext.execute(categoryDelete)

        let accountFetch: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest(entityName: "Account")
        let accountDelete = NSBatchDeleteRequest(fetchRequest: accountFetch)
        try? viewContext.execute(accountDelete)

        let goalFetch: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest(entityName: "Goal")
        let goalDelete = NSBatchDeleteRequest(fetchRequest: goalFetch)
        try? viewContext.execute(goalDelete)

        viewContext.reset()
    }
}

// MARK: - Test Data Factory

extension TestCoreDataStack {

    /// Creates a test account
    @MainActor
    func createTestAccount(
        name: String = "Test Account",
        currency: String = "USD"
    ) -> Account {
        let account = Account(context: viewContext)
        account.id = UUID()
        account.name = name
        account.currency = currency
        account.createdAt = Date()
        return account
    }

    /// Creates a test category
    @MainActor
    func createTestCategory(
        name: String = "Test Category",
        colorHex: String = "#3B82F6"
    ) -> PortfolioJournal.Category {
        let category = PortfolioJournal.Category(context: viewContext)
        category.id = UUID()
        category.name = name
        category.colorHex = colorHex
        return category
    }

    /// Creates a test investment source
    @MainActor
    func createTestSource(
        name: String = "Test Source",
        account: Account? = nil,
        category: PortfolioJournal.Category? = nil
    ) -> InvestmentSource {
        let source = InvestmentSource(context: viewContext)
        source.id = UUID()
        source.name = name
        source.createdAt = Date()
        source.account = account
        source.category = category
        return source
    }

    /// Creates a test snapshot
    @MainActor
    func createTestSnapshot(
        source: InvestmentSource,
        value: Decimal,
        date: Date = Date(),
        contribution: Decimal? = nil
    ) -> Snapshot {
        let snapshot = Snapshot(context: viewContext)
        snapshot.id = UUID()
        snapshot.value = NSDecimalNumber(decimal: value)
        snapshot.date = date
        snapshot.source = source
        if let contribution = contribution {
            snapshot.contribution = NSDecimalNumber(decimal: contribution)
        }
        return snapshot
    }

    /// Creates a test goal
    @MainActor
    func createTestGoal(
        name: String = "Test Goal",
        targetAmount: Decimal,
        account: Account? = nil,
        targetDate: Date? = nil
    ) -> Goal {
        let goal = Goal(context: viewContext)
        goal.id = UUID()
        goal.name = name
        goal.targetAmount = NSDecimalNumber(decimal: targetAmount)
        goal.createdAt = Date()
        goal.isActive = true
        goal.account = account
        goal.targetDate = targetDate
        return goal
    }

    /// Saves the context
    func save() throws {
        if viewContext.hasChanges {
            try viewContext.save()
        }
    }
}
