use crate::model::pending_actions::PendingAction;
use crate::model::reward_codes::ClaimRewardCodeResult;
use crate::{mutate_state, RewardCodes, RuntimeState};
use canister_api_macros::update;
use canister_tracing_macros::trace;
use icp_dispenser_bot::handle_direct_message::*;
use types::{BotMessage, MessageContent, MessageContentInitial, TextContent, UserId};

#[update(msgpack = true)]
#[trace]
fn handle_direct_message(args: Args) -> Response {
    mutate_state(|state| handle_message(args, state))
}

fn handle_message(args: Args, state: &mut RuntimeState) -> Response {
    let caller: UserId = state.env.caller().into();
    let now = state.env.now();

    let (text, action) = if let Some(code) = try_extract_code(args.content) {
        match state.data.reward_codes.claim(code, caller, now) {
            ClaimRewardCodeResult::Success(transfer_args) => (
                "Code claimed successfully! Please wait while your ICP is transferred.",
                Some(PendingAction::IcpTransfer(caller, transfer_args, [0; 32])),
            ),
            ClaimRewardCodeResult::UserAlreadyClaimed => ("You have already claimed a reward!", None),
            ClaimRewardCodeResult::CodeAlreadyClaimed => ("That code has already been claimed!", None),
            ClaimRewardCodeResult::CodeExpired => ("That code has expired!", None),
            ClaimRewardCodeResult::CodeNotFound => ("That code is not valid!", None),
        }
    } else {
        (
            "Please send a text message containing only your reward code (eg. '1234ABCD')",
            None,
        )
    };

    if let Some(a) = action {
        state.data.pending_actions.add(a, now);
    }

    Success(SuccessResult {
        bot_name: state.data.bot_name.clone(),
        bot_display_name: None,
        messages: vec![BotMessage {
            thread_root_message_id: None,
            content: MessageContentInitial::Text(TextContent { text: text.to_string() }),
            message_id: None,
            block_level_markdown: None,
        }],
    })
}

fn try_extract_code(content: MessageContent) -> Option<String> {
    if let MessageContent::Text(TextContent { text }) = content {
        RewardCodes::validate(&text).then_some(text.to_ascii_uppercase())
    } else {
        None
    }
}
