// Copyright 2023 Salesforce, Inc. All rights reserved.
use pdk_core::logger;

use crate::error::CorsError;
use crate::model::cors::CorsResourceTypeEnum;
use crate::model::resource::protected_resource::ResponseType;

///
/// Defines a CORS Resource
///
pub trait CorsResource {
    fn main_request(&self, rt: &MainRequest) -> Result<ResponseType, CorsError>;
    fn simple_request(&self, rt: &SimpleRequest) -> Result<ResponseType, CorsError>;
    fn no_cors_request(&self, rt: &NoCorsRequest) -> Result<ResponseType, CorsError>;
    fn preflight_request(&self, rt: &PreflightRequest) -> Result<ResponseType, CorsError>;
}

///
/// Defines a CORS Request and it's ability to access a defined resource taken from configuration.
///
pub trait CorsRequestType {
    fn access_resource(&self, visitor: &CorsResourceTypeEnum) -> Result<ResponseType, CorsError>;
}

#[derive(Debug, PartialEq, Eq)]
pub struct MainRequest {
    origin: String,
}

#[derive(Debug, PartialEq, Eq)]
pub struct SimpleRequest {
    origin: String,
}

#[derive(Debug, Default, PartialEq, Eq)]
pub struct PreflightRequest {
    origin: String,
    request_method: String,
    request_headers: Vec<String>,
}

#[derive(Debug, Default, PartialEq, Eq)]
pub struct NoCorsRequest {}

impl MainRequest {
    pub(crate) fn new(origin: &str) -> Self {
        Self {
            origin: origin.to_string(),
        }
    }

    pub(crate) fn origin(&self) -> &String {
        &self.origin
    }
}

impl SimpleRequest {
    pub(crate) fn new(origin: &str) -> Self {
        Self {
            origin: origin.to_string(),
        }
    }

    pub(crate) fn origin(&self) -> &String {
        &self.origin
    }
}

impl PreflightRequest {
    pub(crate) fn new(origin: &str, request_method: &str, request_headers: &[String]) -> Self {
        Self {
            origin: origin.to_string(),
            request_method: request_method.to_string(),
            request_headers: request_headers.to_vec(),
        }
    }

    pub(crate) fn origin(&self) -> &str {
        &self.origin
    }

    pub(crate) fn method(&self) -> &str {
        self.request_method.as_str()
    }

    pub(crate) fn headers(&self) -> &[String] {
        self.request_headers.as_slice()
    }
}

///
/// A CORS Main Request is any request that does not meet the Simple Request requirements nor
/// is a Preflight Request.
///
impl CorsRequestType for MainRequest {
    fn access_resource(&self, visitor: &CorsResourceTypeEnum) -> Result<ResponseType, CorsError> {
        logger::debug!("Processing incoming request as Main CORS Request");
        visitor.main_request(self)
    }
}

///
/// A CORS Simple Request is any request that meet the following three requirements:
///
/// - MUST be one of the following methods: GET, PUT, HEAD
/// - MUST contain a specific set of headers. Refer to <link_to_class> to have the list.
/// - In case of Content-Type header, it MUST have a set of values. Refer to <link_to_class>
///
impl CorsRequestType for SimpleRequest {
    fn access_resource(&self, visitor: &CorsResourceTypeEnum) -> Result<ResponseType, CorsError> {
        logger::debug!("Processing incoming request as Simple CORS Request");
        visitor.simple_request(self)
    }
}

///
/// A No CORS Request is any request that does not contain the Origin header in its attributes.
///
impl CorsRequestType for NoCorsRequest {
    fn access_resource(&self, visitor: &CorsResourceTypeEnum) -> Result<ResponseType, CorsError> {
        logger::debug!("Processing incoming request as No CORS Request");
        visitor.no_cors_request(self)
    }
}

impl CorsRequestType for PreflightRequest {
    fn access_resource(&self, visitor: &CorsResourceTypeEnum) -> Result<ResponseType, CorsError> {
        logger::debug!("Processing incoming request as Preflight CORS Request");
        visitor.preflight_request(self)
    }
}
