import Combine
import Foundation

enum CallLogDirection: String, Sendable {
    case outgoing
    case incoming
    case missed
    case rejected

    var label: String {
        switch self {
        case .outgoing:
            return "Outgoing"
        case .incoming:
            return "Incoming"
        case .missed:
            return "Missed"
        case .rejected:
            return "Rejected"
        }
    }

    var isMissed: Bool {
        self == .missed
    }

    var isIncoming: Bool {
        self == .incoming || self == .missed
    }

    var isOutgoing: Bool {
        self == .outgoing || self == .rejected
    }
}

struct CallLogEntry: Identifiable, Equatable, Sendable {
    let id: String
    let opponentKey: String
    let name: String
    let initials: String
    let colorIndex: Int
    let direction: CallLogDirection
    let durationSec: Int
    let timestamp: Int64
    let dateText: String

    var isMissed: Bool {
        direction.isMissed
    }

    var subtitleText: String {
        let base = direction.label
        guard durationSec > 0 else { return base }
        return "\(base) (\(Self.formattedDuration(seconds: durationSec)))"
    }

    private static func formattedDuration(seconds: Int) -> String {
        let safe = max(seconds, 0)
        let minutes = safe / 60
        let secs = safe % 60
        return String(format: "%d:%02d", minutes, secs)
    }
}

@MainActor
final class CallsViewModel: ObservableObject {
    @Published private(set) var recentCalls: [CallLogEntry] = []
    @Published var isEditing = false

    private let maxEntries: Int

    private static let timeFormatter: DateFormatter = {
        let formatter = DateFormatter()
        formatter.dateFormat = "HH:mm"
        return formatter
    }()

    private static let dayFormatter: DateFormatter = {
        let formatter = DateFormatter()
        formatter.dateFormat = "EEE"
        return formatter
    }()

    private static let dateFormatter: DateFormatter = {
        let formatter = DateFormatter()
        formatter.dateFormat = "dd.MM.yy"
        return formatter
    }()

    init(maxEntries: Int = 300) {
        self.maxEntries = max(1, maxEntries)
    }

    func reload() {
        let dialogs = DialogRepository.shared.sortedDialogs
        guard !dialogs.isEmpty else {
            if !recentCalls.isEmpty {
                recentCalls = []
            }
            return
        }

        let ownKey = SessionManager.shared.currentPublicKey
        var entries: [CallLogEntry] = []
        entries.reserveCapacity(64)

        for dialog in dialogs where !dialog.isSavedMessages {
            let messages = MessageRepository.shared.messages(for: dialog.opponentKey)
            guard !messages.isEmpty else { continue }

            for message in messages {
                guard let callAttachment = message.attachments.first(where: { $0.type == .call }) else {
                    continue
                }

                let durationSec = AttachmentPreviewCodec.parseCallDurationSeconds(callAttachment.preview)
                let isOutgoing = ownKey.isEmpty
                    ? (message.fromPublicKey == dialog.account)
                    : (message.fromPublicKey == ownKey)
                let direction = callDirection(isOutgoing: isOutgoing, durationSec: durationSec)

                let title = dialog.opponentTitle.trimmingCharacters(in: .whitespacesAndNewlines)
                let displayName: String
                if title.isEmpty {
                    displayName = String(dialog.opponentKey.prefix(12))
                } else {
                    displayName = title
                }

                let messageId = message.id.isEmpty
                    ? "\(dialog.opponentKey)-\(message.timestamp)-\(entries.count)"
                    : message.id

                entries.append(
                    CallLogEntry(
                        id: messageId,
                        opponentKey: dialog.opponentKey,
                        name: displayName,
                        initials: dialog.initials,
                        colorIndex: dialog.avatarColorIndex,
                        direction: direction,
                        durationSec: durationSec,
                        timestamp: message.timestamp,
                        dateText: Self.formattedTimestamp(message.timestamp)
                    )
                )
            }
        }

        entries.sort {
            if $0.timestamp == $1.timestamp {
                return $0.id > $1.id
            }
            return $0.timestamp > $1.timestamp
        }

        if entries.count > maxEntries {
            entries = Array(entries.prefix(maxEntries))
        }

        if recentCalls != entries {
            recentCalls = entries
        }
    }

    func deleteCallEntry(_ entry: CallLogEntry) {
        guard !entry.id.contains("-") || !entry.id.contains(":") else {
            // Synthetic ID — can't delete from DB, just remove from UI
            recentCalls.removeAll { $0.id == entry.id }
            return
        }
        MessageRepository.shared.deleteMessage(id: entry.id)
        recentCalls.removeAll { $0.id == entry.id }
    }

    func deleteCalls(_ entries: [CallLogEntry]) {
        for entry in entries {
            deleteCallEntry(entry)
        }
        if recentCalls.isEmpty {
            isEditing = false
        }
    }

    private func callDirection(isOutgoing: Bool, durationSec: Int) -> CallLogDirection {
        if durationSec <= 0 {
            return isOutgoing ? .rejected : .missed
        }
        return isOutgoing ? .outgoing : .incoming
    }

    private static func formattedTimestamp(_ timestampMs: Int64) -> String {
        guard timestampMs > 0 else { return "" }
        let date = Date(timeIntervalSince1970: Double(timestampMs) / 1000)
        let now = Date()
        let calendar = Calendar.current

        if calendar.isDateInToday(date) {
            return timeFormatter.string(from: date)
        }
        if calendar.isDateInYesterday(date) {
            return "Yesterday"
        }
        if let days = calendar.dateComponents([.day], from: date, to: now).day, days < 7 {
            return dayFormatter.string(from: date)
        }
        return dateFormatter.string(from: date)
    }
}
