import Foundation
import CoreData

class TagManager: ObservableObject {
    private let persistenceController = PersistenceController.shared

    @Published var allTags: [Tag] = []
    @Published var isLoading: Bool = false
    @Published var errorMessage: String?

    init() {
        fetchTags()
    }

    func fetchTags() {
        isLoading = true
        let context = persistenceController.container.viewContext
        let request: NSFetchRequest<Tag> = Tag.fetchRequest()
        request.sortDescriptors = [NSSortDescriptor(keyPath: \Tag.name, ascending: true)]

        do {
            allTags = try context.fetch(request)
            isLoading = false
        } catch {
            errorMessage = error.localizedDescription
            isLoading = false
        }
    }

    func createTag(name: String, description: String? = nil) -> Tag? {
        let context = persistenceController.container.viewContext

        // Check if tag already exists
        if tagExists(name: name) {
            errorMessage = NSLocalizedString("tag_already_exists", comment: "Tag already exists")
            return nil
        }

        let newTag = Tag(context: context)
        newTag.name = name.trimmingCharacters(in: .whitespacesAndNewlines)
        newTag.slug = generateSlug(from: name)
        newTag.tagDescription = description
        newTag.createdAt = Date()

        do {
            try context.save()
            fetchTags()
            return newTag
        } catch {
            errorMessage = error.localizedDescription
            return nil
        }
    }

    func updateTag(_ tag: Tag, name: String, description: String?) -> Bool {
        let context = persistenceController.container.viewContext

        // Check if name already exists (excluding current tag)
        if name != tag.name && tagExists(name: name) {
            errorMessage = NSLocalizedString("tag_already_exists", comment: "Tag already exists")
            return false
        }

        tag.name = name.trimmingCharacters(in: .whitespacesAndNewlines)
        tag.slug = generateSlug(from: name)
        tag.tagDescription = description

        do {
            try context.save()
            fetchTags()
            return true
        } catch {
            errorMessage = error.localizedDescription
            return false
        }
    }

    func deleteTag(_ tag: Tag) -> Bool {
        let context = persistenceController.container.viewContext

        // Remove tag from all associated links
        if let links = tag.links as? Set<Link> {
            for link in links {
                link.removeFromTags(tag)
            }
        }

        context.delete(tag)

        do {
            try context.save()
            fetchTags()
            return true
        } catch {
            errorMessage = error.localizedDescription
            return false
        }
    }

    func findOrCreateTag(name: String) -> Tag? {
        let trimmedName = name.trimmingCharacters(in: .whitespacesAndNewlines)

        // First, try to find existing tag
        if let existingTag = findTag(name: trimmedName) {
            return existingTag
        }

        // Create new tag if not found
        return createTag(name: trimmedName)
    }

    func findTag(name: String) -> Tag? {
        return allTags.first { $0.name.lowercased() == name.lowercased() }
    }

    func findTagBySlug(_ slug: String) -> Tag? {
        return allTags.first { $0.slug == slug }
    }

    func searchTags(query: String) -> [Tag] {
        if query.isEmpty {
            return allTags
        }

        return allTags.filter { tag in
            tag.name.localizedCaseInsensitiveContains(query) ||
            (tag.tagDescription?.localizedCaseInsensitiveContains(query) ?? false)
        }
    }

    func getTagsWithPrefix(_ prefix: String) -> [Tag] {
        if prefix.isEmpty {
            return allTags
        }

        return allTags.filter { tag in
            tag.name.lowercased().hasPrefix(prefix.lowercased())
        }
    }

    func getMostPopularTags(limit: Int = 10) -> [Tag] {
        return allTags
            .sorted { $0.linkCount > $1.linkCount }
            .prefix(limit)
            .map { $0 }
    }

    func getRecentlyUsedTags(limit: Int = 5) -> [Tag] {
        // Get tags that were recently added to links
        let context = persistenceController.container.viewContext
        let request: NSFetchRequest<Link> = Link.fetchRequest()
        request.sortDescriptors = [NSSortDescriptor(keyPath: \Link.updatedAt, ascending: false)]
        request.fetchLimit = limit * 3 // Fetch more links to get variety of tags

        do {
            let recentLinks = try context.fetch(request)
            var tagCounts: [Tag: Int] = [:]

            for link in recentLinks {
                for tag in link.sortedTags {
                    tagCounts[tag, default: 0] += 1
                }
            }

            return tagCounts
                .sorted { $0.value > $1.value }
                .prefix(limit)
                .map { $0.key }
        } catch {
            return []
        }
    }

    func getTagStatistics() -> TagStatistics {
        let totalTags = allTags.count
        let totalLinks = allTags.reduce(0) { $0 + $1.linkCount }
        let averageLinksPerTag = totalTags > 0 ? Double(totalLinks) / Double(totalTags) : 0.0
        let mostPopularTag = allTags.max { $0.linkCount < $1.linkCount }
        let unusedTags = allTags.filter { $0.linkCount == 0 }

        return TagStatistics(
            totalTags: totalTags,
            totalLinks: totalLinks,
            averageLinksPerTag: averageLinksPerTag,
            mostPopularTag: mostPopularTag,
            unusedTagsCount: unusedTags.count
        )
    }

    func mergeTags(_ sourceTags: [Tag], into targetTag: Tag) -> Bool {
        let context = persistenceController.container.viewContext

        do {
            // Move all links from source tags to target tag
            for sourceTag in sourceTags {
                if let links = sourceTag.links as? Set<Link> {
                    for link in links {
                        link.removeFromTags(sourceTag)
                        link.addToTags(targetTag)
                    }
                }

                // Delete source tag
                context.delete(sourceTag)
            }

            try context.save()
            fetchTags()
            return true
        } catch {
            errorMessage = error.localizedDescription
            return false
        }
    }

    func cleanupUnusedTags() -> Int {
        let unusedTags = allTags.filter { $0.linkCount == 0 }
        let context = persistenceController.container.viewContext

        for tag in unusedTags {
            context.delete(tag)
        }

        do {
            try context.save()
            fetchTags()
            return unusedTags.count
        } catch {
            errorMessage = error.localizedDescription
            return 0
        }
    }

    // MARK: - Private Helper Methods

    private func tagExists(name: String) -> Bool {
        return findTag(name: name) != nil
    }

    private func generateSlug(from name: String) -> String {
        return name
            .lowercased()
            .trimmingCharacters(in: .whitespacesAndNewlines)
            .replacingOccurrences(of: " ", with: "-")
            .replacingOccurrences(of: "[^a-z0-9-]", with: "", options: .regularExpression)
    }
}

// MARK: - Supporting Types

struct TagStatistics {
    let totalTags: Int
    let totalLinks: Int
    let averageLinksPerTag: Double
    let mostPopularTag: Tag?
    let unusedTagsCount: Int
}

struct TagSuggestion {
    let name: String
    let reason: String
    let confidence: Double
}

// MARK: - Tag Suggestions

extension TagManager {
    func suggestTagsForLink(_ link: Link) -> [TagSuggestion] {
        var suggestions: [TagSuggestion] = []

        // Suggest based on URL domain
        if let url = link.originalURL, let domain = extractDomain(from: url) {
            if let domainTag = suggestTagFromDomain(domain) {
                suggestions.append(domainTag)
            }
        }

        // Suggest based on link type
        let typeTag = TagSuggestion(
            name: link.linkTypeEnum.displayName.lowercased(),
            reason: NSLocalizedString("suggested_based_on_type", comment: "Suggested based on link type"),
            confidence: 0.8
        )
        suggestions.append(typeTag)

        // Suggest based on alias keywords
        let aliasKeywords = extractKeywords(from: link.alias)
        for keyword in aliasKeywords {
            if keyword.count > 2 {
                suggestions.append(TagSuggestion(
                    name: keyword,
                    reason: NSLocalizedString("suggested_from_alias", comment: "Suggested from alias"),
                    confidence: 0.6
                ))
            }
        }

        // Suggest based on description
        if let description = link.linkDescription {
            let descriptionKeywords = extractKeywords(from: description)
            for keyword in descriptionKeywords.prefix(3) {
                if keyword.count > 3 {
                    suggestions.append(TagSuggestion(
                        name: keyword,
                        reason: NSLocalizedString("suggested_from_description", comment: "Suggested from description"),
                        confidence: 0.5
                    ))
                }
            }
        }

        return suggestions
            .filter { suggestion in
                !allTags.contains { $0.name.lowercased() == suggestion.name.lowercased() }
            }
            .sorted { $0.confidence > $1.confidence }
    }

    private func extractDomain(from url: String) -> String? {
        guard let urlObj = URL(string: url),
              let host = urlObj.host else { return nil }

        let components = host.components(separatedBy: ".")
        return components.count >= 2 ? components[components.count - 2] : host
    }

    private func suggestTagFromDomain(_ domain: String) -> TagSuggestion? {
        let commonDomainTags: [String: String] = [
            "github": "code",
            "stackoverflow": "programming",
            "youtube": "video",
            "twitter": "social",
            "facebook": "social",
            "linkedin": "professional",
            "medium": "articles",
            "wikipedia": "reference",
            "google": "search",
            "apple": "tech",
            "microsoft": "tech",
            "amazon": "shopping"
        ]

        if let tagName = commonDomainTags[domain.lowercased()] {
            return TagSuggestion(
                name: tagName,
                reason: NSLocalizedString("suggested_from_domain", comment: "Suggested from domain"),
                confidence: 0.7
            )
        }

        return TagSuggestion(
            name: domain,
            reason: NSLocalizedString("suggested_from_domain", comment: "Suggested from domain"),
            confidence: 0.6
        )
    }

    private func extractKeywords(from text: String) -> [String] {
        let words = text
            .lowercased()
            .components(separatedBy: CharacterSet.alphanumerics.inverted)
            .filter { !$0.isEmpty }

        // Filter out common stop words
        let stopWords = Set(["the", "a", "an", "and", "or", "but", "in", "on", "at", "to", "for", "of", "with", "by"])

        return words.filter { !stopWords.contains($0) }
    }
}
