import Foundation
import XCTest

/// Seeds backend data for integration tests via API calls.
///
/// All methods require a valid auth token from a `TestSession`.
/// Created resources are tracked so `TestDataCleaner` can remove them in teardown.
enum TestDataSeeder {

    // MARK: - Residence Seeding

    /// Create a residence with just a name. Returns the residence or fails the test.
    @discardableResult
    static func createResidence(
        token: String,
        name: String? = nil,
        file: StaticString = #filePath,
        line: UInt = #line
    ) -> TestResidence {
        let residenceName = name ?? "Test Residence \(uniqueSuffix())"
        guard let residence = TestAccountAPIClient.createResidence(token: token, name: residenceName) else {
            XCTFail("Failed to seed residence '\(residenceName)'", file: file, line: line)
            preconditionFailure("seeding failed — see XCTFail above")
        }
        return residence
    }

    /// Create a residence with address fields populated.
    @discardableResult
    static func createResidenceWithAddress(
        token: String,
        name: String? = nil,
        street: String = "123 Test St",
        city: String = "Testville",
        state: String = "TX",
        postalCode: String = "78701",
        file: StaticString = #filePath,
        line: UInt = #line
    ) -> TestResidence {
        let residenceName = name ?? "Addressed Residence \(uniqueSuffix())"
        guard let residence = TestAccountAPIClient.createResidence(
            token: token,
            name: residenceName,
            fields: [
                "street_address": street,
                "city": city,
                "state_province": state,
                "postal_code": postalCode
            ]
        ) else {
            XCTFail("Failed to seed residence with address '\(residenceName)'", file: file, line: line)
            preconditionFailure("seeding failed — see XCTFail above")
        }
        return residence
    }

    // MARK: - Task Seeding

    /// Create a task in a residence. Returns the task or fails the test.
    @discardableResult
    static func createTask(
        token: String,
        residenceId: Int,
        title: String? = nil,
        fields: [String: Any] = [:],
        file: StaticString = #filePath,
        line: UInt = #line
    ) -> TestTask {
        let taskTitle = title ?? "Test Task \(uniqueSuffix())"
        guard let task = TestAccountAPIClient.createTask(
            token: token,
            residenceId: residenceId,
            title: taskTitle,
            fields: fields
        ) else {
            XCTFail("Failed to seed task '\(taskTitle)'", file: file, line: line)
            preconditionFailure("seeding failed — see XCTFail above")
        }
        return task
    }

    /// Create a task with a due date.
    @discardableResult
    static func createTaskWithDueDate(
        token: String,
        residenceId: Int,
        title: String? = nil,
        daysFromNow: Int = 7,
        file: StaticString = #filePath,
        line: UInt = #line
    ) -> TestTask {
        let dueDate = Calendar.current.date(byAdding: .day, value: daysFromNow, to: Date())!
        let formatter = ISO8601DateFormatter()
        formatter.formatOptions = [.withFullDate]
        let dueDateStr = formatter.string(from: dueDate)

        return createTask(
            token: token,
            residenceId: residenceId,
            title: title ?? "Due Task \(uniqueSuffix())",
            fields: ["due_date": dueDateStr],
            file: file,
            line: line
        )
    }

    /// Create a cancelled task (create then cancel via API).
    @discardableResult
    static func createCancelledTask(
        token: String,
        residenceId: Int,
        title: String? = nil,
        file: StaticString = #filePath,
        line: UInt = #line
    ) -> TestTask {
        let task = createTask(token: token, residenceId: residenceId, title: title ?? "Cancelled Task \(uniqueSuffix())", file: file, line: line)
        guard let cancelled = TestAccountAPIClient.cancelTask(token: token, id: task.id) else {
            XCTFail("Failed to cancel seeded task \(task.id)", file: file, line: line)
            preconditionFailure("seeding failed — see XCTFail above")
        }
        return cancelled
    }

    // MARK: - Contractor Seeding

    /// Create a contractor. Returns the contractor or fails the test.
    @discardableResult
    static func createContractor(
        token: String,
        name: String? = nil,
        fields: [String: Any] = [:],
        file: StaticString = #filePath,
        line: UInt = #line
    ) -> TestContractor {
        let contractorName = name ?? "Test Contractor \(uniqueSuffix())"
        guard let contractor = TestAccountAPIClient.createContractor(
            token: token,
            name: contractorName,
            fields: fields
        ) else {
            XCTFail("Failed to seed contractor '\(contractorName)'", file: file, line: line)
            preconditionFailure("seeding failed — see XCTFail above")
        }
        return contractor
    }

    /// Create a contractor with contact info.
    @discardableResult
    static func createContractorWithContact(
        token: String,
        name: String? = nil,
        company: String = "Test Co",
        phone: String = "555-0100",
        email: String? = nil,
        file: StaticString = #filePath,
        line: UInt = #line
    ) -> TestContractor {
        let contractorName = name ?? "Contact Contractor \(uniqueSuffix())"
        let contactEmail = email ?? "\(uniqueSuffix())@contractor.test"
        return createContractor(
            token: token,
            name: contractorName,
            fields: ["company": company, "phone": phone, "email": contactEmail],
            file: file,
            line: line
        )
    }

    // MARK: - Document Seeding

    /// Create a document in a residence. Returns the document or fails the test.
    @discardableResult
    static func createDocument(
        token: String,
        residenceId: Int,
        title: String? = nil,
        documentType: String = "general",
        fields: [String: Any] = [:],
        file: StaticString = #filePath,
        line: UInt = #line
    ) -> TestDocument {
        let docTitle = title ?? "Test Doc \(uniqueSuffix())"
        guard let document = TestAccountAPIClient.createDocument(
            token: token,
            residenceId: residenceId,
            title: docTitle,
            documentType: documentType,
            fields: fields
        ) else {
            XCTFail("Failed to seed document '\(docTitle)'", file: file, line: line)
            preconditionFailure("seeding failed — see XCTFail above")
        }
        return document
    }

    // MARK: - Composite Scenarios

    /// Create a residence with N tasks already in it. Returns (residence, [tasks]).
    static func createResidenceWithTasks(
        token: String,
        residenceName: String? = nil,
        taskCount: Int = 3,
        file: StaticString = #filePath,
        line: UInt = #line
    ) -> (residence: TestResidence, tasks: [TestTask]) {
        let residence = createResidence(token: token, name: residenceName, file: file, line: line)
        var tasks: [TestTask] = []
        for i in 1...taskCount {
            let task = createTask(token: token, residenceId: residence.id, title: "Task \(i) \(uniqueSuffix())", file: file, line: line)
            tasks.append(task)
        }
        return (residence, tasks)
    }

    /// Create a residence with a contractor and a document. Returns all three.
    static func createFullResidence(
        token: String,
        file: StaticString = #filePath,
        line: UInt = #line
    ) -> (residence: TestResidence, task: TestTask, contractor: TestContractor, document: TestDocument) {
        let residence = createResidence(token: token, file: file, line: line)
        let task = createTask(token: token, residenceId: residence.id, file: file, line: line)
        let contractor = createContractor(token: token, file: file, line: line)
        let document = createDocument(token: token, residenceId: residence.id, file: file, line: line)
        return (residence, task, contractor, document)
    }

    // MARK: - Private

    private static func uniqueSuffix() -> String {
        let stamp = Int(Date().timeIntervalSince1970) % 100000
        let random = Int.random(in: 100...999)
        return "\(stamp)_\(random)"
    }
}
