use std::marker::PhantomData;
use time::OffsetDateTime;
use super::{
    ToAssign, WithContextRequirements, WithCredentialsHandle, WithOutput, WithTargetDataRepresentation,
    WithoutContextRequirements, WithoutCredentialsHandle, WithoutOutput, WithoutTargetDataRepresentation,
};
use crate::{
    DataRepresentation, OwnedSecurityBuffer, SecurityStatus, ServerRequestFlags, ServerResponseFlags, SspiPackage,
};
pub type EmptyAcceptSecurityContext<'a, C> = AcceptSecurityContext<
    'a,
    C,
    WithoutCredentialsHandle,
    WithoutContextRequirements,
    WithoutTargetDataRepresentation,
    WithoutOutput,
>;
pub type FilledAcceptSecurityContext<'a, C> = AcceptSecurityContext<
    'a,
    C,
    WithCredentialsHandle,
    WithContextRequirements,
    WithTargetDataRepresentation,
    WithOutput,
>;
#[derive(Debug, Clone)]
pub struct AcceptSecurityContextResult {
    pub status: SecurityStatus,
    pub flags: ServerResponseFlags,
    pub expiry: Option<OffsetDateTime>,
}
pub struct AcceptSecurityContext<
    'a,
    CredsHandle,
    CredsHandleSet,
    ContextRequirementsSet,
    TargetDataRepresentationSet,
    OutputSet,
> where
    CredsHandleSet: ToAssign,
    ContextRequirementsSet: ToAssign,
    TargetDataRepresentationSet: ToAssign,
    OutputSet: ToAssign,
{
    phantom_creds_use_set: PhantomData<CredsHandleSet>,
    phantom_context_req_set: PhantomData<ContextRequirementsSet>,
    phantom_data_repr_set: PhantomData<TargetDataRepresentationSet>,
    phantom_output_set: PhantomData<OutputSet>,
    pub credentials_handle: Option<&'a mut CredsHandle>,
    pub context_requirements: ServerRequestFlags,
    pub target_data_representation: DataRepresentation,
    pub output: &'a mut [OwnedSecurityBuffer],
    pub input: Option<&'a mut [OwnedSecurityBuffer]>,
}
impl<
        'a,
        CredsHandle,
        CredsHandleSet: ToAssign,
        ContextRequirementsSet: ToAssign,
        TargetDataRepresentationSet: ToAssign,
        OutputSet: ToAssign,
    >
    AcceptSecurityContext<
        'a,
        CredsHandle,
        CredsHandleSet,
        ContextRequirementsSet,
        TargetDataRepresentationSet,
        OutputSet,
    >
{
    pub(crate) fn new() -> Self {
        AcceptSecurityContext {
            phantom_creds_use_set: PhantomData,
            phantom_context_req_set: PhantomData,
            phantom_data_repr_set: PhantomData,
            phantom_output_set: PhantomData,
            credentials_handle: None,
            context_requirements: ServerRequestFlags::empty(),
            target_data_representation: DataRepresentation::Network,
            output: &mut [],
            input: None,
        }
    }
    pub fn with_credentials_handle(
        self,
        credentials_handle: &'a mut CredsHandle,
    ) -> AcceptSecurityContext<
        'a,
        CredsHandle,
        WithCredentialsHandle,
        ContextRequirementsSet,
        TargetDataRepresentationSet,
        OutputSet,
    > {
        AcceptSecurityContext {
            phantom_creds_use_set: PhantomData,
            phantom_context_req_set: PhantomData,
            phantom_data_repr_set: PhantomData,
            phantom_output_set: PhantomData,
            credentials_handle: Some(credentials_handle),
            context_requirements: self.context_requirements,
            target_data_representation: self.target_data_representation,
            output: self.output,
            input: self.input,
        }
    }
    pub fn with_context_requirements(
        self,
        context_requirements: ServerRequestFlags,
    ) -> AcceptSecurityContext<
        'a,
        CredsHandle,
        CredsHandleSet,
        WithContextRequirements,
        TargetDataRepresentationSet,
        OutputSet,
    > {
        AcceptSecurityContext {
            phantom_creds_use_set: PhantomData,
            phantom_context_req_set: PhantomData,
            phantom_data_repr_set: PhantomData,
            phantom_output_set: PhantomData,
            credentials_handle: self.credentials_handle,
            context_requirements,
            target_data_representation: self.target_data_representation,
            output: self.output,
            input: self.input,
        }
    }
    pub fn with_target_data_representation(
        self,
        target_data_representation: DataRepresentation,
    ) -> AcceptSecurityContext<
        'a,
        CredsHandle,
        CredsHandleSet,
        ContextRequirementsSet,
        WithTargetDataRepresentation,
        OutputSet,
    > {
        AcceptSecurityContext {
            phantom_creds_use_set: PhantomData,
            phantom_context_req_set: PhantomData,
            phantom_data_repr_set: PhantomData,
            phantom_output_set: PhantomData,
            credentials_handle: self.credentials_handle,
            context_requirements: self.context_requirements,
            target_data_representation,
            output: self.output,
            input: self.input,
        }
    }
    pub fn with_output(
        self,
        output: &'a mut [OwnedSecurityBuffer],
    ) -> AcceptSecurityContext<
        'a,
        CredsHandle,
        CredsHandleSet,
        ContextRequirementsSet,
        TargetDataRepresentationSet,
        WithOutput,
    > {
        AcceptSecurityContext {
            phantom_creds_use_set: PhantomData,
            phantom_context_req_set: PhantomData,
            phantom_data_repr_set: PhantomData,
            phantom_output_set: PhantomData,
            credentials_handle: self.credentials_handle,
            context_requirements: self.context_requirements,
            target_data_representation: self.target_data_representation,
            output,
            input: self.input,
        }
    }
    pub fn with_input(self, input: &'a mut [OwnedSecurityBuffer]) -> Self {
        Self {
            input: Some(input),
            ..self
        }
    }
}
impl<'b, 'a: 'b, CredsHandle> FilledAcceptSecurityContext<'a, CredsHandle> {
    pub(crate) fn full_transform<CredsHandle2>(
        self,
        credentials_handle: Option<&'b mut CredsHandle2>,
    ) -> FilledAcceptSecurityContext<'b, CredsHandle2> {
        AcceptSecurityContext {
            phantom_creds_use_set: PhantomData,
            phantom_context_req_set: PhantomData,
            phantom_data_repr_set: PhantomData,
            phantom_output_set: PhantomData,
            credentials_handle,
            context_requirements: self.context_requirements,
            target_data_representation: self.target_data_representation,
            output: self.output,
            input: self.input,
        }
    }
}
impl<'a, CredsHandle> FilledAcceptSecurityContext<'a, CredsHandle> {
    pub fn execute<AuthData>(
        self,
        inner: SspiPackage<'_, CredsHandle, AuthData>,
    ) -> crate::Result<AcceptSecurityContextResult> {
        inner.accept_security_context_impl(self)
    }
    pub(crate) fn transform(self) -> FilledAcceptSecurityContext<'a, CredsHandle> {
        AcceptSecurityContext {
            phantom_creds_use_set: PhantomData,
            phantom_context_req_set: PhantomData,
            phantom_data_repr_set: PhantomData,
            phantom_output_set: PhantomData,
            credentials_handle: self.credentials_handle,
            context_requirements: self.context_requirements,
            target_data_representation: self.target_data_representation,
            output: self.output,
            input: self.input,
        }
    }
}