import Foundation
import SwiftUICore
import Security
import UIKit
import Combine


//let username = "user1"

// Сохраняем токены
//KeychainService.shared.save("access_token_value", forKey: "access_token", service: username)
//KeychainService.shared.save("refresh_token_value", forKey: "refresh_token", service: username)

// Получаем токены
//let accessToken = KeychainService.shared.get(forKey: "access_token", service: username)
//let refreshToken = KeychainService.shared.get(forKey: "refresh_token", service: username)

// получение всех пользователей
//let users = KeychainService.shared.getAllServices()
//print("Все пользователи: \(users)")

// Удаление всех пользователей
//KeychainService.shared.deleteAll()

// удаление по одному
//KeychainService.shared.delete(forKey: "access_token", service: username)
//KeychainService.shared.delete(forKey: "refresh_token", service: username)


class KeychainService {
    
    static let shared = KeychainService()
    
    private init() {}
    
    func save(_ value: String, forKey key: String, service: String) {
        guard let data = value.data(using: .utf8) else { return }
        
        let query: [String: Any] = [
            kSecClass as String: kSecClassGenericPassword,
            kSecAttrService as String: service, // ключ группировки
            kSecAttrAccount as String: key,
            kSecValueData as String: data
        ]
        
        SecItemDelete(query as CFDictionary)
        let status = SecItemAdd(query as CFDictionary, nil)
        if status != errSecSuccess {
            print("Error saving to Keychain: \(status)")
        }
    }
    
    func get(forKey key: String, service: String) -> String? {
        let query: [String: Any] = [
            kSecClass as String: kSecClassGenericPassword,
            kSecAttrService as String: service,
            kSecAttrAccount as String: key,
            kSecReturnData as String: true,
            kSecMatchLimit as String: kSecMatchLimitOne
        ]
        
        var dataTypeRef: AnyObject?
        let status = SecItemCopyMatching(query as CFDictionary, &dataTypeRef)
        if status == errSecSuccess,
           let data = dataTypeRef as? Data,
           let value = String(data: data, encoding: .utf8) {
            return value
        }
        return nil
    }
    
    func getAllServices() -> [String] {
        let query: [String: Any] = [
            kSecClass as String: kSecClassGenericPassword,
            kSecReturnAttributes as String: true,
            kSecMatchLimit as String: kSecMatchLimitAll
        ]
        
        var result: AnyObject?
        let status = SecItemCopyMatching(query as CFDictionary, &result)
        
        guard status == errSecSuccess, let items = result as? [[String: Any]] else {
            return []
        }
        
        // Собираем все уникальные service (username)
        var services = Set<String>()
        for item in items {
            if let service = item[kSecAttrService as String] as? String {
                services.insert(service)
            }
        }
        
        return Array(services)
    }
    
    func delete(forKey key: String, service: String) {
        let query: [String: Any] = [
            kSecClass as String: kSecClassGenericPassword,
            kSecAttrService as String: service,
            kSecAttrAccount as String: key
        ]
        SecItemDelete(query as CFDictionary)
    }
    
    /// Удалить все записи Keychain, сохранённые этим приложением
    func deleteAll() {
        let query: [String: Any] = [
            kSecClass as String: kSecClassGenericPassword
        ]
        let status = SecItemDelete(query as CFDictionary)
        if status != errSecSuccess && status != errSecItemNotFound {
            print("Error deleting all Keychain items: \(status)")
        }
    }
}

class AvatarCacheService {
    static let shared = AvatarCacheService()
    private let fileManager = FileManager.default
    private var baseCacheDirectory: URL? {
        fileManager.urls(for: .cachesDirectory, in: .userDomainMask).first?.appendingPathComponent("avatar_cache")
    }

    private init() {}

    private func cacheDirectory(for userId: String) -> URL? {
        baseCacheDirectory?.appendingPathComponent(userId)
    }

    private func filePath(forKey key: String, userId: String) -> URL? {
        cacheDirectory(for: userId)?.appendingPathComponent(key)
    }

    func getImage(forKey key: String, userId: String) -> UIImage? {
        guard let url = filePath(forKey: key, userId: userId),
              fileManager.fileExists(atPath: url.path),
              let data = try? Data(contentsOf: url) else {
            return nil
        }
        return UIImage(data: data)
    }

    func saveImage(_ image: UIImage, forKey key: String, userId: String) {
        guard let directory = cacheDirectory(for: userId),
              let url = filePath(forKey: key, userId: userId),
              let data = image.jpegData(compressionQuality: 0.8) else {
            return
        }

        if !fileManager.fileExists(atPath: directory.path) {
            try? fileManager.createDirectory(at: directory, withIntermediateDirectories: true, attributes: nil)
        }

        try? data.write(to: url)
    }

    func clearCache(forUserId userId: String) {
        guard let directory = cacheDirectory(for: userId) else { return }
        
        // Try to delete files inside first, ignoring errors
        if let fileUrls = try? fileManager.contentsOfDirectory(at: directory, includingPropertiesForKeys: nil, options: []) {
            for fileUrl in fileUrls {
                try? fileManager.removeItem(at: fileUrl)
            }
        }
        
        // Then try to delete the directory itself
        try? fileManager.removeItem(at: directory)
    }

    func clearAllCache() {
        guard let directory = baseCacheDirectory else { return }
        try? fileManager.removeItem(at: directory)
    }
    
    func getAllCachedUserIds() -> [String] {
        guard let baseDir = baseCacheDirectory else { return [] }
        do {
            let directoryContents = try fileManager.contentsOfDirectory(at: baseDir, includingPropertiesForKeys: nil, options: .skipsHiddenFiles)
            return directoryContents.map { $0.lastPathComponent }
        } catch {
            // This can happen if the directory doesn't exist yet, which is not an error.
            return []
        }
    }

    func sizeOfCache(forUserId userId: String) -> Int64 {
        guard let directory = cacheDirectory(for: userId) else { return 0 }
        return directorySize(url: directory)
    }

    func sizeOfAllCache() -> Int64 {
        guard let directory = baseCacheDirectory else { return 0 }
        return directorySize(url: directory)
    }

    private func directorySize(url: URL) -> Int64 {
        let contents: [URL]
        do {
            contents = try fileManager.contentsOfDirectory(at: url, includingPropertiesForKeys: [.fileSizeKey], options: .skipsHiddenFiles)
        } catch {
            return 0
        }

        var totalSize: Int64 = 0
        for url in contents {
            let fileSize = (try? url.resourceValues(forKeys: [.fileSizeKey]))?.fileSize ?? 0
            totalSize += Int64(fileSize)
        }
        return totalSize
    }
}

class ImageLoader: ObservableObject {
    @Published var image: UIImage?
    
    private let url: URL
    private let fileId: String
    private let userId: String
    private var cancellable: AnyCancellable?
    private let cache = AvatarCacheService.shared

    init(url: URL, fileId: String, userId: String) {
        self.url = url
        self.fileId = fileId
        self.userId = userId
    }

    deinit {
        cancellable?.cancel()
    }

    func load() {
        if let cachedImage = cache.getImage(forKey: fileId, userId: userId) {
            self.image = cachedImage
            return
        }

        cancellable = URLSession.shared.dataTaskPublisher(for: url)
            .map { UIImage(data: $0.data) }
            .replaceError(with: nil)
            .receive(on: DispatchQueue.main)
            .sink { [weak self] loadedImage in
                guard let self = self, let image = loadedImage else { return }
                self.image = image
                self.cache.saveImage(image, forKey: self.fileId, userId: self.userId)
            }
    }
}

struct CachedAvatarView<Placeholder: View>: View {
    @StateObject private var loader: ImageLoader
    private let placeholder: Placeholder

    init(url: URL, fileId: String, userId: String, @ViewBuilder placeholder: () -> Placeholder) {
        self.placeholder = placeholder()
        _loader = StateObject(wrappedValue: ImageLoader(url: url, fileId: fileId, userId: userId))
    }

    var body: some View {
        content
            .onAppear(perform: loader.load)
    }

    private var content: some View {
        Group {
            if let image = loader.image {
                Image(uiImage: image)
                    .resizable()
            } else {
                placeholder
            }
        }
    }
}
