import Foundation
import SwiftUI

/// A task someone has to get done.
struct Task: Identifiable, Codable, Hashable {
    let id: UUID
    var title: String
    var isDone: Bool = false
    var dueDate: Date?

    init(title: String, dueDate: Date? = nil) {
        self.id = UUID()
        self.title = title
        self.dueDate = dueDate
    }
}

enum TaskError: Error {
    case emptyTitle
    case notFound(id: UUID)
}

protocol TaskStore {
    func load() async throws -> [Task]
    func save(_ tasks: [Task]) async throws
}

/* Keeps tasks in a JSON file
   inside the documents directory. */
actor FileTaskStore: TaskStore {
    private let url: URL
    private let encoder = JSONEncoder()

    init(fileName: String = "tasks.json") {
        let directory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
        self.url = directory.appendingPathComponent(fileName)
    }

    func load() async throws -> [Task] {
        guard FileManager.default.fileExists(atPath: url.path) else { return [] }
        let data = try Data(contentsOf: url)
        return try JSONDecoder().decode([Task].self, from: data)
    }

    func save(_ tasks: [Task]) async throws {
        let data = try encoder.encode(tasks)
        try data.write(to: url, options: .atomic)
    }
}

@MainActor
final class TaskList: ObservableObject {
    @Published private(set) var tasks: [Task] = []
    private let store: any TaskStore

    init(store: any TaskStore = FileTaskStore()) {
        self.store = store
    }

    func add(_ title: String) throws {
        let trimmed = title.trimmingCharacters(in: .whitespaces)
        if trimmed.isEmpty { throw TaskError.emptyTitle }
        tasks.append(Task(title: trimmed))
    }

    func toggle(_ task: Task) {
        guard let index = tasks.firstIndex(of: task) else { return }
        tasks[index].isDone.toggle()
    }

    var remaining: Int {
        tasks.filter { !$0.isDone }.count
    }
}

struct TaskListView: View {
    @StateObject private var list = TaskList()
    @State private var draft = ""

    var body: some View {
        NavigationStack {
            List(list.tasks) { task in
                Text(task.title)
                    .strikethrough(task.isDone)
                    .onTapGesture { list.toggle(task) }
            }
            .navigationTitle("Tasks (\(list.remaining))")
        }
    }
}

let limit = 1_000
let mask = 0xFF
let ratio = 0.75
let message = """
    You have \(limit) tasks left.
    """
