use std::{fmt::Debug, hash::Hash};

use syn::{Expr, Local, Path, Stmt, Type};

use crate::wir::{
    IntoSyn, WBasicType, WElementaryType, WExpr, WExprCall, WExprHighCall, WGeneralType, WIdent,
    WIndexedExpr, WIndexedIdent, WItemImplTrait, WMacroableStmt, WPanicResult, WPanicResultType,
    WPartialGeneralType, WSsaLocal, WStmt, WTacLocal, WType,
};

pub trait YStage {
    type AssignTypes: ZAssignTypes + Clone + Debug + Hash;
    type InputType: IntoSyn<Type> + Clone + Debug + Hash;
    type OutputType: IntoSyn<Type> + Clone + Debug + Hash;
    type FnResult: IntoSyn<Expr> + Clone + Debug + Hash;
    type Local: IntoSyn<Local> + Clone + Debug + Hash;
    type ItemImplTrait: IntoSyn<Path> + Clone + Debug + Hash;
}

#[derive(Clone, Debug, Hash)]
pub struct YTac;

impl YStage for YTac {
    type AssignTypes = ZTac;
    type InputType = WType<WBasicType>;
    type OutputType = WBasicType;
    type FnResult = WIdent;
    type Local = WTacLocal<WPartialGeneralType>;
    type ItemImplTrait = WItemImplTrait;
}

#[derive(Clone, Debug, Hash)]
pub struct YNonindexed;

impl YStage for YNonindexed {
    type AssignTypes = ZNonindexed;
    type InputType = WType<WBasicType>;
    type OutputType = WBasicType;
    type FnResult = WIdent;
    type Local = WTacLocal<WPartialGeneralType>;
    type ItemImplTrait = WItemImplTrait;
}

#[derive(Clone, Debug, Hash)]
pub struct YTotal;

impl YStage for YTotal {
    type AssignTypes = ZTotal;
    type InputType = WType<WBasicType>;
    type OutputType = WPanicResultType<WBasicType>;
    type FnResult = WPanicResult;
    type Local = WTacLocal<WPartialGeneralType>;
    type ItemImplTrait = WItemImplTrait;
}

#[derive(Clone, Debug, Hash)]
pub struct YSsa;

impl YStage for YSsa {
    type AssignTypes = ZSsa;
    type InputType = WType<WBasicType>;
    type OutputType = WPanicResultType<WBasicType>;
    type FnResult = WPanicResult;
    type Local = WSsaLocal<WPartialGeneralType>;
    type ItemImplTrait = WItemImplTrait;
}

#[derive(Clone, Debug, Hash)]
pub struct YInferred;

impl YStage for YInferred {
    type AssignTypes = ZSsa;
    type InputType = WType<WBasicType>;
    type OutputType = WPanicResultType<WBasicType>;
    type FnResult = WPanicResult;
    type Local = WSsaLocal<WGeneralType<WBasicType>>;
    type ItemImplTrait = WItemImplTrait;
}

#[derive(Clone, Debug, Hash)]
pub struct YConverted;

impl YStage for YConverted {
    type AssignTypes = ZConverted;
    type InputType = WType<WElementaryType>;
    type OutputType = WPanicResultType<WElementaryType>;
    type FnResult = WPanicResult;
    type Local = WSsaLocal<WGeneralType<WElementaryType>>;
    type ItemImplTrait = WItemImplTrait;
}

#[derive(Clone, Debug, Hash)]
pub struct ZTac;

impl ZAssignTypes for ZTac {
    type Stmt = WMacroableStmt<ZTac>;
    type FundamentalType = WBasicType;
    type AssignLeft = WIndexedIdent;
    type AssignRight = WIndexedExpr<WExprHighCall>;
    type IfPolarity = WNoIfPolarity;
}

#[derive(Clone, Debug, Hash)]
pub struct ZNonindexed;

impl ZAssignTypes for ZNonindexed {
    type Stmt = WMacroableStmt<ZNonindexed>;
    type FundamentalType = WBasicType;
    type AssignLeft = WIdent;
    type AssignRight = WExpr<WExprHighCall>;
    type IfPolarity = WNoIfPolarity;
}

#[derive(Clone, Debug, Hash)]
pub struct ZTotal;

impl ZAssignTypes for ZTotal {
    type Stmt = WStmt<ZTotal>;
    type FundamentalType = WBasicType;
    type AssignLeft = WIdent;
    type AssignRight = WExpr<WExprHighCall>;
    type IfPolarity = WNoIfPolarity;
}

#[derive(Clone, Debug, Hash)]
pub struct ZSsa;

impl ZAssignTypes for ZSsa {
    type Stmt = WStmt<ZSsa>;
    type FundamentalType = WBasicType;
    type AssignLeft = WIdent;
    type AssignRight = WExpr<WExprHighCall>;
    type IfPolarity = WNoIfPolarity;
}

#[derive(Clone, Debug, Hash)]
pub struct ZConverted;

impl ZAssignTypes for ZConverted {
    type Stmt = WStmt<ZConverted>;
    type FundamentalType = WElementaryType;
    type AssignLeft = WIdent;
    type AssignRight = WExpr<WExprCall>;
    type IfPolarity = WNoIfPolarity;
}

pub trait ZIfPolarity: IntoSyn<Path> + Clone + Debug + Hash {}

pub trait ZAssignTypes {
    type Stmt: IntoSyn<Stmt> + Clone + Debug + Hash;
    type FundamentalType: IntoSyn<Type> + Clone + Debug + Hash;
    type AssignLeft: IntoSyn<Expr> + Clone + Debug + Hash;
    type AssignRight: IntoSyn<Expr> + Clone + Debug + Hash;
    type IfPolarity: ZIfPolarity;
}

#[derive(Clone, Debug, Hash)]
pub struct WNoIfPolarity;

impl IntoSyn<Path> for WNoIfPolarity {
    fn into_syn(self) -> Path {
        syn_path::path!(::mck::forward::Test::into_bool)
    }
}

impl ZIfPolarity for WNoIfPolarity {}
