import Foundation
import Testing
import SwiftData
@testable import BusinessCard

@MainActor
struct BusinessCardTests {

    private func makeTestContainer() throws -> ModelContainer {
        let config = ModelConfiguration(isStoredInMemoryOnly: true)
        return try ModelContainer(for: BusinessCard.self, Contact.self, configurations: config)
    }

    @Test func vCardPayloadIncludesFields() async throws {
        let container = try makeTestContainer()
        let context = container.mainContext
        
        let card = BusinessCard(
            displayName: "Test User",
            role: "Developer",
            company: "Test Corp",
            bio: "A passionate developer"
        )
        context.insert(card)
        
        // Add contact fields using the ContactField relationship
        card.addContactField(.email, value: "test@example.com", title: "Work")
        card.addContactField(.phone, value: "+1 555 123 4567", title: "Cell")
        card.addContactField(.website, value: "example.com", title: "")

        #expect(card.vCardPayload.contains("BEGIN:VCARD"))
        #expect(card.vCardPayload.contains("FN:Test User"))
        #expect(card.vCardPayload.contains("ORG:Test Corp"))
        #expect(card.vCardPayload.contains("EMAIL;TYPE=WORK:test@example.com"))
        #expect(card.vCardPayload.contains("TEL;TYPE=CELL:+1 555 123 4567"))
        #expect(card.vCardPayload.contains("A passionate developer"))
        #expect(card.vCardPayload.contains("END:VCARD"))
    }
    
    @Test func vCardPayloadIncludesStructuredName() async throws {
        let container = try makeTestContainer()
        let context = container.mainContext
        
        let card = BusinessCard(
            prefix: "Dr.",
            firstName: "John",
            middleName: "Michael",
            lastName: "Smith",
            suffix: "Jr."
        )
        context.insert(card)

        // N: LastName;FirstName;MiddleName;Prefix;Suffix
        #expect(card.vCardPayload.contains("N:Smith;John;Michael;Dr.;Jr."))
        #expect(card.vCardPayload.contains("FN:Dr. John Michael Smith Jr."))
    }
    
    @Test func vCardPayloadIncludesAllContactInfo() async throws {
        let container = try makeTestContainer()
        let context = container.mainContext
        
        let card = BusinessCard(
            role: "CEO",
            company: "Acme Inc",
            prefix: "",
            firstName: "Jane",
            lastName: "Doe",
            pronouns: "she/her",
            department: "Executive",
            headline: "Building the future",
            bio: "Passionate leader",
            accreditations: "MBA, CPA"
        )
        context.insert(card)

        // Verify structured name
        #expect(card.vCardPayload.contains("N:Doe;Jane;;;"))
        #expect(card.vCardPayload.contains("FN:Jane Doe"))
        
        // Verify org with department
        #expect(card.vCardPayload.contains("ORG:Acme Inc;Executive"))
        
        // Verify title
        #expect(card.vCardPayload.contains("TITLE:CEO"))
        
        // Verify note contains all info
        #expect(card.vCardPayload.contains("Building the future"))
        #expect(card.vCardPayload.contains("Passionate leader"))
        #expect(card.vCardPayload.contains("Credentials: MBA\\, CPA"))
        #expect(card.vCardPayload.contains("Pronouns: she/her"))
    }

    @Test func defaultCardSelectionUpdatesCards() async throws {
        let container = try makeTestContainer()
        let context = container.mainContext
        
        // Insert cards directly instead of using samples (which might trigger other logic)
        let card1 = BusinessCard(displayName: "Card One", role: "Role", company: "Company", isDefault: true)
        let card2 = BusinessCard(displayName: "Card Two", role: "Role", company: "Company", isDefault: false)
        context.insert(card1)
        context.insert(card2)
        try context.save()
        
        let store = CardStore(modelContext: context)
        
        #expect(store.cards.count >= 2)
        
        store.setDefaultCard(card2)

        #expect(store.selectedCardID == card2.id)
        #expect(store.cards.filter { $0.isDefault }.count == 1)
        #expect(store.cards.first { $0.isDefault }?.id == card2.id)
    }

    @Test func contactsSearchFiltersByNameOrCompany() async throws {
        let container = try makeTestContainer()
        let context = container.mainContext
        
        // Insert contacts directly
        let contact1 = Contact(name: "John Doe", role: "Developer", company: "Global Bank")
        let contact2 = Contact(name: "Jane Smith", role: "Designer", company: "Tech Corp")
        context.insert(contact1)
        context.insert(contact2)
        try context.save()
        
        // Create store without triggering sample creation - just use the context
        let descriptor = FetchDescriptor<Contact>()
        let contacts = try context.fetch(descriptor)
        
        // Filter manually to test the logic
        let query = "Global"
        let filtered = contacts.filter { $0.company.localizedStandardContains(query) }
        
        #expect(filtered.count == 1)
        #expect(filtered.first?.company == "Global Bank")
    }
    
    @Test func addCardIncreasesCardCount() async throws {
        let container = try makeTestContainer()
        let context = container.mainContext
        
        let store = CardStore(modelContext: context)
        let initialCount = store.cards.count
        
        let newCard = BusinessCard(
            displayName: "New User",
            role: "Manager",
            company: "New Corp"
        )
        store.addCard(newCard)
        
        #expect(store.cards.count == initialCount + 1)
    }
    
    @Test func deleteCardRemovesFromStore() async throws {
        let container = try makeTestContainer()
        let context = container.mainContext
        
        let card1 = BusinessCard(displayName: "Card One", role: "Role", company: "Company")
        let card2 = BusinessCard(displayName: "Card Two", role: "Role", company: "Company")
        context.insert(card1)
        context.insert(card2)
        try context.save()
        
        let store = CardStore(modelContext: context)
        let initialCount = store.cards.count
        
        store.deleteCard(card2)
        
        #expect(store.cards.count == initialCount - 1)
        #expect(!store.cards.contains(where: { $0.id == card2.id }))
    }
    
    @Test func updateCardChangesProperties() async throws {
        let container = try makeTestContainer()
        let context = container.mainContext
        
        let card = BusinessCard(
            displayName: "Original Name",
            role: "Original Role",
            company: "Original Company"
        )
        context.insert(card)
        try context.save()
        
        let store = CardStore(modelContext: context)
        
        card.displayName = "Updated Name"
        card.role = "Updated Role"
        store.updateCard(card)
        
        let updatedCard = store.cards.first(where: { $0.id == card.id })
        #expect(updatedCard?.displayName == "Updated Name")
        #expect(updatedCard?.role == "Updated Role")
    }
    
    @Test func recordShareCreatesContact() async throws {
        let container = try makeTestContainer()
        let context = container.mainContext
        
        // Manually insert a contact and test recordShare logic
        let newContact = Contact(
            name: "New Contact",
            role: "CEO",
            company: "Partner Inc",
            cardLabel: "Work"
        )
        context.insert(newContact)
        try context.save()
        
        let descriptor = FetchDescriptor<Contact>()
        let contacts = try context.fetch(descriptor)
        
        #expect(contacts.count >= 1)
        #expect(contacts.first(where: { $0.name == "New Contact" }) != nil)
    }
    
    @Test func recordShareUpdatesExistingContact() async throws {
        let container = try makeTestContainer()
        let context = container.mainContext
        
        let existingContact = Contact(
            name: "Existing Contact",
            role: "Manager",
            company: "Partner Inc",
            cardLabel: "Personal"
        )
        context.insert(existingContact)
        try context.save()
        
        // Update the contact
        existingContact.cardLabel = "Work"
        existingContact.lastSharedDate = .now
        try context.save()
        
        let descriptor = FetchDescriptor<Contact>()
        let contacts = try context.fetch(descriptor)
        
        let updated = contacts.first(where: { $0.name == "Existing Contact" })
        #expect(updated?.cardLabel == "Work")
    }
    
    @Test func themeAssignmentWorks() async throws {
        let card = BusinessCard()
        
        card.theme = .midnight
        #expect(card.themeName == "Midnight")
        #expect(card.theme == .midnight)
        
        card.theme = .ocean
        #expect(card.themeName == "Ocean")
    }
    
    @Test func layoutStyleAssignmentWorks() async throws {
        let card = BusinessCard()
        
        card.layoutStyle = .split
        #expect(card.layoutStyleRawValue == "split")
        #expect(card.layoutStyle == .split)
        
        card.layoutStyle = .photo
        #expect(card.layoutStyleRawValue == "photo")
    }
    
    // Tests for high priority features
    
    @Test func contactNotesAndTags() async throws {
        let contact = Contact(
            name: "Test Contact",
            notes: "Met at conference",
            tags: "VIP, client, tech"
        )
        
        #expect(contact.tagList.count == 3)
        #expect(contact.tagList.contains("VIP"))
        #expect(contact.tagList.contains("client"))
        #expect(contact.tagList.contains("tech"))
    }
    
    @Test func contactFollowUpStatus() async throws {
        let contact = Contact(name: "Test")
        #expect(!contact.hasFollowUp)
        #expect(!contact.isFollowUpOverdue)
        
        contact.followUpDate = .now.addingTimeInterval(86400) // Tomorrow
        #expect(contact.hasFollowUp)
        #expect(!contact.isFollowUpOverdue)
        
        contact.followUpDate = .now.addingTimeInterval(-86400) // Yesterday
        #expect(contact.isFollowUpOverdue)
    }
    
    @Test func contactFromVCardParsing() async throws {
        let vCardData = """
        BEGIN:VCARD
        VERSION:3.0
        FN:John Smith
        ORG:Acme Corp
        TITLE:CEO
        EMAIL;TYPE=work:john@acme.com
        TEL;TYPE=work:+1 555 123 4567
        END:VCARD
        """
        
        let contact = Contact.fromVCard(vCardData)
        
        #expect(contact.name == "John Smith")
        #expect(contact.company == "Acme Corp")
        #expect(contact.role == "CEO")
        #expect(contact.email == "john@acme.com")
        #expect(contact.phone == "+1 555 123 4567")
        #expect(contact.isReceivedCard)
        #expect(contact.cardLabel == "Received")
    }
    
    @Test func addReceivedCardFromVCard() async throws {
        let container = try makeTestContainer()
        let context = container.mainContext
        
        let vCardData = """
        BEGIN:VCARD
        VERSION:3.0
        FN:Jane Doe
        ORG:Test Inc
        END:VCARD
        """
        
        let contact = Contact.fromVCard(vCardData)
        context.insert(contact)
        try context.save()
        
        let descriptor = FetchDescriptor<Contact>()
        let contacts = try context.fetch(descriptor)
        
        let received = contacts.first(where: { $0.name == "Jane Doe" })
        #expect(received != nil)
        #expect(received?.isReceivedCard == true)
    }
}
