//
//  CallView.swift
//  PadelClub
//
//  Created by Razmig Sarkissian on 16/04/2024.
//

import SwiftUI
import LeStorage

struct CallView: View {
    
    struct CallStatusView: View {
        let count: Int
        let total: Int
        let startDate: Date?
        var title: String = "convoquées au bon horaire"

        var body: some View {
            VStack(spacing: 0) {
                HStack {
                    if let startDate {
                        Text(startDate.formattedAsHourMinute())
                    } else {
                        Text("Aucun horaire").font(.body)
                    }
                    Spacer()
                    Text("\(count.formatted())/\(total.formatted())")
                }
                .font(.largeTitle)
                HStack {
                    if let startDate {
                        Text(startDate.formatted(.dateTime.weekday().day(.twoDigits).month().year()))
                    }
                    Spacer()
                    Text(title)
                }
                .font(.caption)
                .foregroundColor(.secondary)
            }
        }
    }
    
    @EnvironmentObject var dataStore: DataStore
    
    @EnvironmentObject var networkMonitor: NetworkMonitor
    @Environment(Tournament.self) var tournament: Tournament
    
    var teams: [TeamRegistration]
    let callDate: Date
    let matchFormat: MatchFormat
    let roundLabel: String
    let displayContext: SummoningDisplayContext
    
    @State private var contactType: ContactType? = nil
    @State private var sentError: ContactManagerError? = nil

    @State var showSubscriptionView: Bool = false
    @State var showUserCreationView: Bool = false

    @State var summonParamByMessage: Bool = false
    @State var summonParamReSummon: Bool = false
    
    let simpleMode : Bool
    
    init(teams: [TeamRegistration], callDate: Date, matchFormat: MatchFormat, roundLabel: String) {
        self.teams = teams
        self.callDate = callDate
        self.matchFormat = matchFormat
        self.roundLabel = roundLabel
        self.simpleMode = false
        self.displayContext = .footer
    }
    
    init(teams: [TeamRegistration]) {
        self.teams = teams
        self.callDate = Date()
        self.matchFormat = MatchFormat.nineGames
        self.roundLabel = ""
        self.simpleMode = true
        self.displayContext = .footer
    }
    
    init(team: TeamRegistration, displayContext: SummoningDisplayContext) {
        self.teams = [team]
        let expectedSummonDate = team.expectedSummonDate()
        self.displayContext = displayContext

        if let expectedSummonDate, let initialMatch = team.initialMatch() {
            self.callDate = expectedSummonDate
            self.matchFormat = initialMatch.matchFormat
            self.roundLabel = initialMatch.roundTitle() ?? "tableau"
            self.simpleMode = false
        } else if let expectedSummonDate, let initialGroupStage = team.groupStageObject() {
            self.callDate = expectedSummonDate
            self.matchFormat = initialGroupStage.matchFormat
            self.roundLabel = "poule"
            self.simpleMode = false
        } else {
            self.callDate = Date()
            self.matchFormat = MatchFormat.nineGames
            self.roundLabel = ""
            self.simpleMode = true
        }
    }
    
    var tournamentStore: TournamentStore? {
        return self.tournament.tournamentStore
    }
    
    var messageSentFailed: Binding<Bool> {
        Binding {
            sentError != nil
        } set: { newValue in
            if newValue == false {
                sentError = nil
            }
        }
    }
    
    private func _called(_ calledTeams: [TeamRegistration], _ success: Bool) {
        if simpleMode {
            return
        }
        if success {
            calledTeams.forEach { team in
                team.callDate = callDate
                if reSummon {
                    team.confirmationDate = nil
                }
            }
            do {
                try self.tournamentStore?.teamRegistrations.addOrUpdate(contentOfs: calledTeams)
            } catch {
                Logger.error(error)
            }
        }
    }
    
    func finalMessage(reSummon: Bool, forcedEmptyMessage: Bool) -> String {
        if simpleMode || forcedEmptyMessage {
            let signature = dataStore.user.getSummonsMessageSignature() ?? dataStore.user.defaultSignature(tournament)
            return "\n\n\n\n" + signature
        }

        return ContactType.callingMessage(tournament: tournament, startDate: callDate, roundLabel: roundLabel, matchFormat: matchFormat, reSummon: reSummon)
    }
    
    var reSummon: Bool {
        if simpleMode {
            return false
        }
        return self.teams.allSatisfy({ $0.called() })
    }
    
    var mainWord: String {
        if simpleMode {
            return "Contacter"
        } else {
            return "Convoquer"
        }
    }
    
    var body: some View {
        Group {
            switch displayContext {
            case .footer:
                _footerStyleView()
            case .menu:
                _menuStyleView()
            }
        }
        .alert("Un problème est survenu", isPresented: messageSentFailed) {
            Button("OK") {
            }
            
            if case .uncalledTeams(let uncalledTeams) = sentError {
                NavigationLink("Voir les équipes non contactées") {
                    TeamsCallingView(teams: uncalledTeams)
                        .environment(tournament)
                }
            }
            
        } message: {
            Text(_networkErrorMessage)
        }
        .sheet(item: $contactType) { contactType in
            Group {
                switch contactType {
                case .message(_, let recipients, let body, _):
                    MessageComposeView(recipients: recipients, body: body) { result in
                        switch result {
                        case .cancelled:
                            break
                        case .failed:
                            self.sentError = .messageFailed
                        case .sent:
                            let calledTeams = teams.filter { $0.getPhoneNumbers().isEmpty == false }
                            let uncalledTeams = teams.filter { $0.getPhoneNumbers().isEmpty }

                            if networkMonitor.connected == false {
                                if uncalledTeams.isEmpty == false, calledTeams.isEmpty == false {
                                    self.sentError = .uncalledTeams(uncalledTeams)
                                } else {
                                    self.sentError = .messageNotSent
                                }
                            } else {
                                if uncalledTeams.isEmpty == false, calledTeams.isEmpty == false {
                                    self.sentError = .uncalledTeams(uncalledTeams)
                                } else if uncalledTeams.isEmpty == false, calledTeams.isEmpty {
                                    self._called(uncalledTeams, true)
                                }
                                self._called(calledTeams, true)
                            }
                        @unknown default:
                            break
                        }
                    }
                case .mail(_, let recipients, let bccRecipients, let body, let subject, _):
                    MailComposeView(recipients: recipients, bccRecipients: bccRecipients, body: body, subject: subject) { result in
                        switch result {
                        case .cancelled, .saved:
                            self.contactType = nil
                        case .failed:
                            self.contactType = nil
                            self.sentError = .mailFailed
                        case .sent:
                            let calledTeams = teams.filter { $0.getMail().isEmpty == false }
                            let uncalledTeams = teams.filter { $0.getMail().isEmpty }

                            if networkMonitor.connected == false {
                                self.contactType = nil
                                if uncalledTeams.isEmpty == false, calledTeams.isEmpty == false {
                                    self.sentError = .uncalledTeams(uncalledTeams)
                                } else {
                                    self.sentError = .mailNotSent
                                }
                            } else {
                                if uncalledTeams.isEmpty == false, calledTeams.isEmpty == false {
                                    self.sentError = .uncalledTeams(uncalledTeams)
                                } else if uncalledTeams.isEmpty == false, calledTeams.isEmpty {
                                    self._called(uncalledTeams, true)
                                }
                                self._called(calledTeams, true)
                            }
                        @unknown default:
                            break
                        }
                    }
                }
            }
            .tint(.master)
        }
        .sheet(isPresented: self.$showSubscriptionView, content: {
            NavigationStack {
                SubscriptionView(isPresented: self.$showSubscriptionView, showLackOfPlanMessage: true)
                    .environment(\.colorScheme, .light)
            }
        })
        .sheet(isPresented: self.$showUserCreationView, content: {
            NavigationStack {
                LoginView(reason: LoginReason.loginRequiredForFeature) { _ in
                    self.showUserCreationView = false
                    self._payTournamentAndExecute {
                        self._summon(byMessage: self.summonParamByMessage,
                                     reSummon: self.summonParamByMessage)
                    }
                }
            }
        })
    }
    
    private func _footerStyleView() -> some View {
        HStack {
            let callWord : String = (reSummon ? "Reconvoquer" : mainWord)
            if self.teams.count == 1 {
                if simpleMode {
                    Text("\(callWord) cette paire par")
                } else {
                    if let previousCallDate = teams.first?.callDate, Calendar.current.compare(previousCallDate, to: callDate, toGranularity: .minute) != .orderedSame {
                        Text("Reconvoquer \(self.callDate.localizedDate()) par")
                    } else {
                        Text("\(callWord) cette paire par")
                    }
                }
            } else {
                Text("\(callWord) ces \(self.teams.count) paires par")
            }
            
            self._summonMenu(byMessage: true)
            Text("ou")
            self._summonMenu(byMessage: false)
        }
        .font(.subheadline)
        .buttonStyle(.borderless)
    }
    
    private func _menuStyleView() -> some View {
        Menu {
            self._summonMenu(byMessage: true)
            self._summonMenu(byMessage: false)
        } label: {
            let callWord : String = (reSummon ? "Reconvoquer" : mainWord)
            if self.teams.count == 1 {
                if simpleMode {
                    Text("\(callWord) cette paire")
                } else {
                    if let previousCallDate = teams.first?.callDate, Calendar.current.compare(previousCallDate, to: callDate, toGranularity: .minute) != .orderedSame {
                        Text("Reconvoquer \(self.callDate.localizedDate())")
                    } else {
                        Text("\(callWord) cette paire")
                    }
                }
            } else {
                Text("\(callWord) ces \(self.teams.count) paires")
            }
        }
    }

        
    @ViewBuilder
    private func _summonMenu(byMessage: Bool) -> some View {
        if self.reSummon {
            Menu {
                Button(mainWord) {
                    self._summon(byMessage: byMessage, reSummon: false)
                }
                
                Button("Re-convoquer") {
                    self._summon(byMessage: byMessage, reSummon: true)
                }
                
                if simpleMode == false {
                    Divider()
                    Button("Contacter") {
                        self._summon(byMessage: byMessage, reSummon: false, forcedEmptyMessage: true)
                    }
                }

            } label: {
                Text(byMessage ? "sms" : "mail")
                    .underline()
            }
        } else {
            FooterButtonView(byMessage ? "sms" : "mail") {
                self._summon(byMessage: byMessage, reSummon: false)
            }
        }
    }
    
    private func _summon(byMessage: Bool, reSummon: Bool, forcedEmptyMessage: Bool = false) {
        self.summonParamByMessage = byMessage
        self.summonParamReSummon = reSummon
        self._verifyUser {
            self._payTournamentAndExecute {
                if byMessage {
                    self._contactByMessage(reSummon: reSummon, forcedEmptyMessage: forcedEmptyMessage)
                } else {
                    self._contactByMail(reSummon: reSummon, forcedEmptyMessage: forcedEmptyMessage)
                }
            }
        }
    }

    fileprivate func _verifyUser(_ handler: () -> ()) {
        if StoreCenter.main.userId != nil {
            handler()
        } else {
            self.showUserCreationView = true
        }
    }
    
    fileprivate func _payTournamentAndExecute(_ handler: () -> ()) {
        do {
            try self.tournament.payIfNecessary()
            handler()
        } catch {
            self.showSubscriptionView = true
        }
    }
    
    fileprivate func _contactByMessage(reSummon: Bool, forcedEmptyMessage: Bool) {
        self.contactType = .message(date: callDate,
                                    recipients: teams.flatMap { $0.getPhoneNumbers() },
                                    body: finalMessage(reSummon: reSummon, forcedEmptyMessage: forcedEmptyMessage),
                                    tournamentBuild: nil)
    }
    
    fileprivate func _contactByMail(reSummon: Bool, forcedEmptyMessage: Bool) {
        self.contactType = .mail(date: callDate,
                            recipients: tournament.umpireMail(),
                            bccRecipients: teams.flatMap { $0.getMail() },
                                 body: finalMessage(reSummon: reSummon, forcedEmptyMessage: forcedEmptyMessage),
                                 subject: tournament.tournamentTitle(hideSenior: true),
                            tournamentBuild: nil)
    }

    private var _networkErrorMessage: String {
        ContactManagerError.getNetworkErrorMessage(sentError: sentError, networkMonitorConnected: networkMonitor.connected)
    }

}

struct TeamCallView: View {
    @Environment(Tournament.self) var tournament: Tournament
    let team: TeamRegistration
    var action: (() -> Void)?

    var body: some View {
        NavigationLink {
            CallMenuOptionsView(team: team, action: action)
                .environment(tournament)
        } label: {
            TeamRowView(team: team, displayCallDate: true)
        }
        .buttonStyle(.plain)
        .listRowView(isActive: team.confirmed(), color: .green, hideColorVariation: true)
    }
}
