pub mod counted;
pub mod traced;

use std::{error::Error, fmt::Display, rc::Rc};

use sha2::{Digest, Sha256};

use crate::func::{context::*, *};
use crate::rcore::*;
use crate::rstd::{cast::*, inject::*, typeless::*};

pub struct NoDiagnostic;

impl<'a, T: Monad<'a>> Diagnostic<'a, T> for NoDiagnostic {
    fn after<'b, A>(fa: T::F<A>, _event: impl 'b + FnOnce() -> String) -> T::F<A> {
        fa
    }

    fn before<'b, A>(fa: T::F<A>, _event: impl 'b + FnOnce() -> String) -> T::F<A> {
        fa
    }

    fn wrapped<'b, A>(fa: T::F<A>, _event: impl 'b + FnOnce() -> String) -> T::F<A> {
        fa
    }
}

pub struct TestContextPlain;

#[derive(Debug)]
pub enum TestLookupError<'a> {
    Typeless(TypelessError<'a>),
    Cast(CastError<'a>),
    EmptyResolverAccess(Address),
}

impl<'a> From<TypelessError<'a>> for TestLookupError<'a> {
    fn from(value: TypelessError<'a>) -> Self {
        Self::Typeless(value)
    }
}

impl<'a> From<CastError<'a>> for TestLookupError<'a> {
    fn from(value: CastError<'a>) -> Self {
        Self::Cast(value)
    }
}

impl<'a> Display for TestLookupError<'a> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::Typeless(typeless_error) => {
                write!(f, "typeless lookup failure: {}", typeless_error)
            }
            Self::Cast(cast_error) => {
                write!(f, "cast failure: {}", cast_error)
            }
            Self::EmptyResolverAccess(address) => {
                write!(f, "accessed an empty resolved at address {}", address)
            }
        }
    }
}

impl<'a> Error for TestLookupError<'a> {}

impl<'a> FunctorContext<'a> for TestContextPlain {
    type T = instances::solo::SoloInstance;
}

impl<'a> FallibleCtx<'a> for TestContextPlain {
    type Fallible = instances::result::ResultFailAny;
}

impl<'a> Context<'a> for TestContextPlain {
    type _Tm = Self::T;

    type D = NoDiagnostic;

    type LookupError = TestLookupError<'a>;

    fn hash(s: &[u8]) -> Hash {
        let mut hasher = Sha256::new();
        hasher.update(s);
        hasher.finalize().into()
    }
}

pub struct EmptyResolver;

impl<'a> Resolver<'a, TestContextPlain> for EmptyResolver {
    fn resolve(self: std::rc::Rc<Self>, address: Address) -> HashResolution<'a, TestContextPlain> {
        Err(TestLookupError::EmptyResolverAccess(address))
    }
}

struct EmptyInject;

impl<'a, Ctx: Context<'a>> Inject<'a, Ctx> for EmptyInject {
    fn inject<A: 'a>(&self, fa: Wrapped<'a, Ctx, A>) -> Wrapped<'a, Ctx, A> {
        fa
    }
}

pub fn reparse<'a, A: Mentionable<'a, TestContextPlain>>(mentionable: Rc<A>) -> A {
    Rc::new(EmptyInject)
        .inject_mentionable(mentionable.as_ref())
        .expect("re-parsing failed")
}