// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

pub mod consumer_group;
pub mod mux;
pub mod snapshot;
pub mod stream;
pub mod user;

use bytes::Bytes;
use iggy_common::Either;
use left_right::{Absorb, ReadHandle, WriteHandle};
use std::cell::UnsafeCell;
use std::sync::Arc;

pub struct WriteCell<T, O>
where
    T: Absorb<O>,
{
    inner: UnsafeCell<WriteHandle<T, O>>,
}

impl<T, O> std::fmt::Debug for WriteCell<T, O>
where
    T: Absorb<O>,
{
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("WriteCell").finish_non_exhaustive()
    }
}

impl<T, O> WriteCell<T, O>
where
    T: Absorb<O>,
{
    pub const fn new(write: WriteHandle<T, O>) -> Self {
        Self {
            inner: UnsafeCell::new(write),
        }
    }

    /// # Panics
    /// Panics if the inner `UnsafeCell` pointer is null (should never happen
    /// unless the writer was not properly initialized).
    pub fn apply(&self, cmd: O) {
        let hdl = unsafe {
            self.inner
                .get()
                .as_mut()
                .expect("[apply]: called on uninit writer")
        };
        hdl.append(cmd).publish();
    }
}

/// Parses type-erased input into a command. Macro-generated.
/// Returns:
/// - `Ok(Either::Left(cmd))` if applicable
/// - `Ok(Either::Right(input))` to pass ownership back
/// - `Err(error)` for malformed payload/parse errors
#[allow(clippy::missing_errors_doc)]
pub trait Command {
    type Cmd;
    type Input;
    type Error;

    fn parse(input: Self::Input) -> Result<Either<Self::Cmd, Self::Input>, Self::Error>;
}

/// Per-command handler for a given state type.
/// Each command struct implements this for the state it mutates.
/// Returns a `Bytes` response that will be threaded into the Reply message.
pub trait StateHandler {
    type State;
    fn apply(&self, state: &mut Self::State) -> Bytes;
}

#[derive(Debug)]
pub struct LeftRight<T, C>
where
    T: Absorb<C>,
{
    write: Option<WriteCell<T, C>>,
    #[allow(unused)]
    read: Arc<ReadHandle<T>>,
}

impl<T, C> LeftRight<T, C>
where
    T: Absorb<C>,
{
    /// # Panics
    /// Panics if the read handle has been dropped (should never happen in normal operation).
    pub fn read<F, R>(&self, f: F) -> R
    where
        F: FnOnce(&T) -> R,
    {
        let guard = self.read.enter().expect("read handle should be accessible");
        f(&*guard)
    }
}

impl<T> From<T> for LeftRight<T, <T as Command>::Cmd>
where
    T: Absorb<<T as Command>::Cmd> + Clone + Command,
{
    fn from(inner: T) -> Self {
        let (write, read) = {
            let (w, r) = left_right::new_from_empty(inner);
            (WriteCell::new(w).into(), r.into())
        };
        Self { write, read }
    }
}

impl<T> LeftRight<T, <T as Command>::Cmd>
where
    T: Absorb<<T as Command>::Cmd> + Clone + Command,
{
    /// # Panics
    /// Panics if this is not the owner shard (no write handle available).
    pub fn do_apply(&self, cmd: <T as Command>::Cmd) {
        self.write
            .as_ref()
            .expect("no write handle - not the owner shard")
            .apply(cmd);
    }
}

/// Public interface for state handlers.
#[allow(clippy::missing_errors_doc)]
pub trait State {
    type Output;
    type Input;
    type Error;

    fn apply(&self, input: Self::Input) -> Result<Either<Self::Output, Self::Input>, Self::Error>;
}

#[allow(clippy::missing_errors_doc)]
pub trait StateMachine {
    type Input;
    type Output;
    type Error;
    fn update(&self, input: Self::Input) -> Result<Self::Output, Self::Error>;
}

/// Generates the state's inner struct and wrapper type.
///
/// # Generated items
/// - `{$state}Inner` struct with the specified fields (the data)
/// - `$state` wrapper struct (contains `LeftRight` storage)
/// - `From<LeftRight<...>>` impl for `$state`
/// - `From<{$state}Inner>` impl for `$state`
///
/// The command enum, parsing, dispatch, and Absorb impl are generated
/// by `collect_handlers!` separately, keeping state definition decoupled
/// from the set of operations.
#[macro_export]
macro_rules! define_state {
    (
        $state:ident {
            $($field_name:ident : $field_type:ty),* $(,)?
        }
    ) => {
        paste::paste! {
            #[derive(Debug, Clone, Default)]
            pub struct [<$state Inner>] {
                $(
                    pub $field_name: $field_type,
                )*
                pub last_result: Option<bytes::Bytes>,
            }

            impl [<$state Inner>] {
                pub fn new() -> Self {
                    Self::default()
                }
            }

            #[derive(Debug)]
            pub struct $state {
                inner: $crate::stm::LeftRight<[<$state Inner>], [<$state Command>]>,
            }

            impl From<$crate::stm::LeftRight<[<$state Inner>], [<$state Command>]>> for $state {
                fn from(inner: $crate::stm::LeftRight<[<$state Inner>], [<$state Command>]>) -> Self {
                    Self { inner }
                }
            }

            impl From<[<$state Inner>]> for $state {
                fn from(inner: [<$state Inner>]) -> Self {
                    let left_right: $crate::stm::LeftRight<[<$state Inner>], [<$state Command>]> = inner.into();
                    left_right.into()
                }
            }
        }
    };
}

/// Generates the command enum, parsing, dispatch, State, and Absorb for a state type.
///
/// # Generated items
/// - `{$state}Command` enum with one variant per operation
/// - `Command` impl for `{$state}Inner` (parses `Message<PrepareHeader>`)
/// - `{$state}Inner::dispatch()` method (routes each variant to `StateHandler::apply()`)
/// - `State` impl for `$state` wrapper
/// - `Absorb<{$state}Command>` impl for `{$state}Inner`
///
/// # Requirements
/// Each listed operation must have a corresponding `{Operation}Request` wire type
/// that implements `WireDecode` and `StateHandler<State = {$state}Inner>`.
#[macro_export]
macro_rules! collect_handlers {
    (
        $state:ident {
            $($operation:ident),* $(,)?
        }
    ) => {
        paste::paste! {
            #[derive(Debug, Clone)]
            pub enum [<$state Command>] {
                $(
                    $operation([<$operation Request>]),
                )*
            }

            impl $crate::stm::Command for [<$state Inner>] {
                type Cmd = [<$state Command>];
                type Input = ::iggy_binary_protocol::Message<::iggy_binary_protocol::PrepareHeader>;
                type Error = ::iggy_common::IggyError;

                fn parse(input: Self::Input) -> Result<::iggy_common::Either<Self::Cmd, Self::Input>, Self::Error> {
                    use ::iggy_binary_protocol::WireDecode;
                    use ::iggy_common::Either;
                    use ::iggy_binary_protocol::{Operation, PrepareHeader};
                    match input.header().operation {
                        $(
                            Operation::$operation => {
                                // TODO: FIXME, zero allocation operation construction.
                                let header = *input.header();
                                let body = ::bytes::Bytes::copy_from_slice(
                                    &input.as_slice()[core::mem::size_of::<PrepareHeader>()..header.size as usize]
                                );
                                let cmd = [<$operation Request>]::decode_from(&body)
                                    .map_err(|_| ::iggy_common::IggyError::InvalidCommand)?;
                                Ok(Either::Left([<$state Command>]::$operation(cmd)))
                            },
                        )*
                        _ => Ok(Either::Right(input)),
                    }
                }
            }

            impl [<$state Inner>] {
                fn dispatch(&mut self, cmd: &[<$state Command>]) {
                    self.last_result = Some(match cmd {
                        $(
                            [<$state Command>]::$operation(payload) => {
                                $crate::stm::StateHandler::apply(payload, self)
                            },
                        )*
                    });
                }
            }

            impl $crate::stm::State for $state {
                type Input = <[<$state Inner>] as $crate::stm::Command>::Input;
                type Output = bytes::Bytes;
                type Error = <[<$state Inner>] as $crate::stm::Command>::Error;

                fn apply(&self, input: Self::Input) -> Result<::iggy_common::Either<Self::Output, Self::Input>, Self::Error> {
                    use ::iggy_common::Either;

                    match <[<$state Inner>] as $crate::stm::Command>::parse(input)? {
                        Either::Left(cmd) => {
                            self.inner.do_apply(cmd);
                            let result = self.inner.read(|state| {
                                state.last_result.clone().unwrap_or_default()
                            });
                            Ok(Either::Left(result))
                        }
                        Either::Right(input) => {
                            Ok(Either::Right(input))
                        }
                    }
                }
            }

            impl left_right::Absorb<[<$state Command>]> for [<$state Inner>] {
                fn absorb_first(&mut self, cmd: &mut [<$state Command>], _other: &Self) {
                    self.dispatch(cmd);
                }

                fn absorb_second(&mut self, cmd: [<$state Command>], _other: &Self) {
                    self.dispatch(&cmd);
                }

                fn sync_with(&mut self, first: &Self) {
                    *self = first.clone();
                }
            }
        }
    };
}
