import Foundation

public struct AttachmentRecord: Codable, Identifiable {
    public var id: String { hash }
    public let hash: String
    public let originalName: String
    public let contentType: String
    public let sizeBytes: Int
    public let createdAt: Date
    public let url: String
    public let kind: String?
    public let documentPath: String?
    
    enum CodingKeys: String, CodingKey {
        case hash
        case originalName
        case contentType
        case sizeBytes
        case createdAt
        case url
        case kind
        case documentPath
    }
}

public struct UploadResponse: Codable {
    public let hash: String
    public let contentType: String
    public let attachmentUrl: String
    public let url: String?
    public let kind: String?
}

public struct DocumentRecord: Codable, Identifiable {
    public let id: String
    public let path: String
    public let title: String
    public let hash: String
    public let createdAt: Date?
    public let updatedAt: Date?
}

public struct DocumentSaveRequest: Codable {
    public let content: String
    public let baseHash: String?
}

public struct DocumentSaveResponse: Codable {
    public let status: String
    public let path: String
    public let title: String
    public let hash: String
}

public struct SearchResult: Codable, Identifiable {
    public var id: String { path }
    public let path: String
    public let title: String
}

public struct SyncInitRequest: Codable {
    public let deviceId: String
    public let rootPath: String
}

public struct SyncInitResponse: Codable {
    public let snapshotId: String
    public let serverSnapshot: [FileEntry]

    enum CodingKeys: String, CodingKey {
        case snapshotId
        case serverSnapshot
    }

    public init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        snapshotId = try container.decode(String.self, forKey: .snapshotId)
        serverSnapshot = try container.decodeIfPresent([FileEntry].self, forKey: .serverSnapshot) ?? []
    }
}

public struct FileEntry: Codable {
    public let path: String
    public let hash: String
    public let size: Int64
    public let modified: Date
}

public struct DeltaRequest: Codable {
    public let snapshotId: String
    public let clientDelta: [Change]
}

public struct DeltaResponse: Codable {
    public let serverDelta: [Change]
    public let conflicts: [Conflict]
    public let newSnapshotId: String?

    enum CodingKeys: String, CodingKey {
        case serverDelta
        case conflicts
        case newSnapshotId
    }

    public init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        serverDelta = try container.decodeIfPresent([Change].self, forKey: .serverDelta) ?? []
        conflicts = try container.decodeIfPresent([Conflict].self, forKey: .conflicts) ?? []
        newSnapshotId = try container.decodeIfPresent(String.self, forKey: .newSnapshotId)
    }
}

public struct Change: Codable {
    public let type: String
    public let path: String
    public let oldPath: String?
    public let hash: String?
    public let content: String?
    public let size: Int64?
    public let modified: Date?

    public init(
        type: String,
        path: String,
        oldPath: String?,
        hash: String?,
        content: String? = nil,
        size: Int64?,
        modified: Date?
    ) {
        self.type = type
        self.path = path
        self.oldPath = oldPath
        self.hash = hash
        self.content = content
        self.size = size
        self.modified = modified
    }
}

public struct Conflict: Codable {
    public let path: String
    public let serverHash: String
    public let clientHash: String
    public let serverModified: Date
    public let clientModified: Date
}

public struct APIError: Error, Codable {
    public let error: String
    public let conflictType: String?
    public let existingHash: String?
    public let existingPath: String?
    public let existingUrl: String?

    public init(
        error: String,
        conflictType: String? = nil,
        existingHash: String? = nil,
        existingPath: String? = nil,
        existingUrl: String? = nil
    ) {
        self.error = error
        self.conflictType = conflictType
        self.existingHash = existingHash
        self.existingPath = existingPath
        self.existingUrl = existingUrl
    }

    public var isUploadConflict: Bool {
        conflictType == "document" || conflictType == "attachment"
    }
}

extension APIError: LocalizedError {
    public var errorDescription: String? { error }
}

public struct AttachmentsListResponse: Codable {
    public let attachments: [AttachmentRecord]
}

public struct DocumentsListResponse: Codable {
    public let documents: [DocumentRecord]
}

public struct SearchResponse: Codable {
    public let results: [SearchResult]
}

public enum UploadDuplicateAction: String {
    case overwrite
    case rename
    case reuse
}
