use super::{
    NNS_PROPOSAL_LIST_REPORT_SCHEMA_VERSION, NNS_PROPOSAL_REPORT_SCHEMA_VERSION,
    NnsProposalHostError,
    labels::{nns_proposal_status_text, nns_reward_status_text, nns_topic_text},
    model::{
        NNS_PROPOSAL_REWARD_STATUS_ACCEPT_VOTES_CODE, NNS_PROPOSAL_REWARD_STATUS_INELIGIBLE_CODE,
        NNS_PROPOSAL_REWARD_STATUS_SETTLED_CODE, NNS_PROPOSAL_REWARD_STATUS_SETTLED_LABEL,
        NNS_PROPOSAL_SORT_ASC_LABEL, NNS_PROPOSAL_SORT_DEADLINE_LABEL,
        NNS_PROPOSAL_SORT_DESC_LABEL, NNS_PROPOSAL_SORT_REWARD_STATUS_LABEL,
        NNS_PROPOSAL_SORT_TALLY_TIME_LABEL, NNS_PROPOSAL_SORT_TITLE_LABEL,
        NNS_PROPOSAL_SORT_VOTING_POWER_LABEL, NNS_PROPOSAL_STATUS_EXECUTED_CODE,
        NNS_PROPOSAL_STATUS_EXECUTED_LABEL, NNS_PROPOSAL_STATUS_OPEN_CODE,
        NNS_PROPOSAL_STATUS_OPEN_LABEL, NNS_PROPOSAL_TOPIC_GOVERNANCE_CODE,
        NNS_PROPOSAL_TOPIC_GOVERNANCE_LABEL, NNS_PROPOSAL_TOPIC_PROTOCOL_CANISTER_MANAGEMENT_CODE,
        NNS_PROPOSAL_TOPIC_PROTOCOL_CANISTER_MANAGEMENT_LABEL,
        NNS_PROPOSAL_TOPIC_SUBNET_MANAGEMENT_CODE, NNS_PROPOSAL_VOTE_YES_LABEL,
        NnsProposalListReport, NnsProposalListRequest, NnsProposalListSort, NnsProposalRequest,
        NnsProposalRewardStatusFilter, NnsProposalSortDirection, NnsProposalStatusFilter,
        NnsProposalTopicFilter,
    },
    source::{
        NnsProposalFetchRequest, NnsProposalSource, build_nns_proposal_list_report_with_source,
        build_nns_proposal_report_with_source, nns_proposal_row_from_info,
    },
    text::{nns_proposal_list_report_text, nns_proposal_report_text},
    view::proposal_matches_query,
    wire::{
        NnsGovernanceBallot, NnsNeuronId, NnsProposal, NnsProposalAction, NnsProposalId,
        NnsProposalInfo, NnsProposalTallyWire,
    },
};
use crate::{
    ic_registry::{DEFAULT_MAINNET_ENDPOINT, MAINNET_GOVERNANCE_CANISTER_ID},
    subnet_catalog::MAINNET_NETWORK,
};
use candid::Reserved;

mod detail;
mod labels;
mod list;
mod sorts;

#[derive(Clone, Debug)]
struct FixtureSource {
    expected_status: Vec<i32>,
    expected_reward_status: Vec<i32>,
    proposals: Vec<NnsProposalInfo>,
    proposal: NnsProposalInfo,
}

impl NnsProposalSource for FixtureSource {
    fn fetch_proposals(
        &self,
        _request: &NnsProposalFetchRequest,
        limit: u32,
        before_proposal_id: Option<u64>,
        include_status: &[i32],
        include_reward_status: &[i32],
    ) -> Result<Vec<NnsProposalInfo>, NnsProposalHostError> {
        assert_eq!(limit, 50);
        assert_eq!(before_proposal_id, Some(200));
        assert_eq!(include_status, self.expected_status);
        assert_eq!(include_reward_status, self.expected_reward_status);
        Ok(self.proposals.clone())
    }

    fn fetch_proposal(
        &self,
        _request: &NnsProposalFetchRequest,
        proposal_id: u64,
    ) -> Result<NnsProposalInfo, NnsProposalHostError> {
        assert_eq!(proposal_id, 101);
        Ok(self.proposal.clone())
    }
}

fn proposal_sort_request(sort: NnsProposalListSort) -> NnsProposalListRequest {
    NnsProposalListRequest {
        network: MAINNET_NETWORK.to_string(),
        source_endpoint: DEFAULT_MAINNET_ENDPOINT.to_string(),
        now_unix_secs: 1_700_000_000,
        limit: 50,
        before_proposal_id: Some(200),
        status: NnsProposalStatusFilter::Any,
        reward_status: NnsProposalRewardStatusFilter::Any,
        topic: NnsProposalTopicFilter::Any,
        proposer_neuron_id: None,
        query: None,
        sort,
        sort_direction: sort.default_direction(),
        verbose: false,
    }
}

fn proposal_ids(report: &NnsProposalListReport) -> Vec<u64> {
    report
        .proposals
        .iter()
        .filter_map(|proposal| proposal.proposal_id)
        .collect()
}

fn proposal_info(
    proposal_id: u64,
    topic: i32,
    status: i32,
    title: &str,
    yes: u64,
) -> NnsProposalInfo {
    proposal_info_with_summary(proposal_id, topic, status, title, yes, "Proposal summary")
}

fn proposal_info_with_reward_status(
    proposal_id: u64,
    topic: i32,
    status: i32,
    reward_status: i32,
    title: &str,
    yes: u64,
) -> NnsProposalInfo {
    NnsProposalInfo {
        reward_status,
        ..proposal_info(proposal_id, topic, status, title, yes)
    }
}

fn proposal_info_with_deadline_and_voting_power(
    proposal_id: u64,
    deadline_timestamp_seconds: Option<u64>,
    total_potential_voting_power: Option<u64>,
) -> NnsProposalInfo {
    NnsProposalInfo {
        deadline_timestamp_seconds,
        total_potential_voting_power,
        ..proposal_info(
            proposal_id,
            NNS_PROPOSAL_TOPIC_GOVERNANCE_CODE,
            NNS_PROPOSAL_STATUS_EXECUTED_CODE,
            "Sortable proposal",
            20,
        )
    }
}

fn proposal_info_with_tally_timestamp(
    proposal_id: u64,
    tally_timestamp_seconds: Option<u64>,
) -> NnsProposalInfo {
    let latest_tally = tally_timestamp_seconds.map(|timestamp_seconds| NnsProposalTallyWire {
        timestamp_seconds,
        yes: 20,
        no: 1,
        total: 21,
    });

    NnsProposalInfo {
        latest_tally,
        ..proposal_info(
            proposal_id,
            NNS_PROPOSAL_TOPIC_GOVERNANCE_CODE,
            NNS_PROPOSAL_STATUS_EXECUTED_CODE,
            "Tally timestamp proposal",
            20,
        )
    }
}

fn proposal_info_with_proposer(proposal_id: u64, proposer_neuron_id: u64) -> NnsProposalInfo {
    NnsProposalInfo {
        proposer: Some(NnsNeuronId {
            id: proposer_neuron_id,
        }),
        ..proposal_info(
            proposal_id,
            NNS_PROPOSAL_TOPIC_GOVERNANCE_CODE,
            NNS_PROPOSAL_STATUS_EXECUTED_CODE,
            "Proposer proposal",
            20,
        )
    }
}

fn proposal_info_with_summary(
    proposal_id: u64,
    topic: i32,
    status: i32,
    title: &str,
    yes: u64,
    summary: &str,
) -> NnsProposalInfo {
    NnsProposalInfo {
        id: Some(NnsProposalId { id: proposal_id }),
        status,
        topic,
        ballots: vec![
            (
                2,
                NnsGovernanceBallot {
                    vote: 2,
                    voting_power: 1,
                },
            ),
            (
                1,
                NnsGovernanceBallot {
                    vote: 1,
                    voting_power: yes,
                },
            ),
        ],
        proposal_timestamp_seconds: 1_700_000_000 + proposal_id,
        reward_event_round: 7,
        deadline_timestamp_seconds: Some(1_700_010_000),
        failed_timestamp_seconds: 0,
        reject_cost_e8s: 100_000_000,
        latest_tally: Some(NnsProposalTallyWire {
            no: 1,
            yes,
            total: yes + 1,
            timestamp_seconds: 1_700_000_100,
        }),
        reward_status: 3,
        decided_timestamp_seconds: 1_700_000_200,
        proposal: Some(NnsProposal {
            url: "https://dashboard.internetcomputer.org/proposal/101".to_string(),
            title: Some(title.to_string()),
            action: Some(NnsProposalAction::Motion(Reserved)),
            summary: summary.to_string(),
        }),
        proposer: Some(NnsNeuronId { id: 99 }),
        executed_timestamp_seconds: 1_700_000_300,
        total_potential_voting_power: Some(100),
    }
}
