//
//  Timeline.swift
//  Pachyderm
//
//  Created by Shadowfacts on 9/9/18.
//  Copyright © 2018 Shadowfacts. All rights reserved.
//

import Foundation

public enum Timeline: Equatable, Hashable, Sendable {
    case home
    case `public`(local: Bool)
    case tag(hashtag: String)
    case list(id: String)
    case direct
}

extension Timeline {
    var endpoint: Endpoint {
        switch self {
        case .home:
            return "/api/v1/timelines/home"
        case .public:
            return "/api/v1/timelines/public"
        case let .tag(hashtag):
            return "/api/v1/timelines/tag/\(hashtag)"
        case let .list(id):
            return "/api/v1/timelines/list/\(id)"
        case .direct:
            return "/api/v1/timelines/direct"
        }
    }
    
    func request(range: RequestRange) -> Request<[Status]> {
        var request: Request<[Status]> = Request<[Status]>(method: .get, path: endpoint)
        if case .public(true) = self {
            request.queryParameters.append("local" => true)
        }
        request.range = range
        // 206 can happen when the timeline is being regenerated and therefore is incomplete
        request.additionalAcceptableHTTPCodes = [206]
        return request
    }
}

extension Timeline: Codable {
    public init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let type = try container.decode(String.self, forKey: .type)
        switch type {
        case "home":
            self = .home
        case "public":
            self = .public(local: try container.decode(Bool.self, forKey: .local))
        case "tag":
            self = .tag(hashtag: try container.decode(String.self, forKey: .hashtag))
        case "list":
            self = .list(id: try container.decode(String.self, forKey: .listID))
        case "direct":
            self = .direct
        default:
            throw DecodingError.dataCorruptedError(forKey: CodingKeys.type, in: container, debugDescription: "Timeline type must be one of 'home', 'local', 'tag', 'list', or 'direct'")
        }
    }
    
    public func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        switch self {
        case .home:
            try container.encode("home", forKey: .type)
        case let .public(local):
            try container.encode("public", forKey: .type)
            try container.encode(local, forKey: .local)
        case let .tag(hashtag):
            try container.encode("tag", forKey: .type)
            try container.encode(hashtag, forKey: .hashtag)
        case let .list(id):
            try container.encode("list", forKey: .type)
            try container.encode(id, forKey: .listID)
        case .direct:
            try container.encode("direct", forKey: .type)
        }
    }
    
    enum CodingKeys: String, CodingKey {
        case type
        case local
        case hashtag
        case listID
    }
}
