// Copyright Two Neutron Stars Incorporated and contributors
// SPDX-License-Identifier: BlueOak-1.0.0

//! Merged-level symbol partitions.
//!
//! Stage 3 of the symbolique pipeline. Merging combines symbols from multiple
//! files into a single workspace-wide symbol table:
//!
//! - **MergedSymbols**: Index-only partition with `SymbolEntry` index entries
//!   pointing to shapes in the shared `Symbols` partition

use {
  super::records::SymbolEntry,
  crate::core::{
    Ident, SymbolPath, SymbolVisibility, Value, Visibility,
  },
  laburnum::{
    Ident as LaburnumIdent,
    database::{Partition, PartitionKey},
  },
  std::{hash::Hash, marker::PhantomData},
};

/// Merged-level symbol index partition (index-only).
///
/// Stores only index entries that reference shapes in the shared `Symbols`
/// partition. Each entry contains a span and a handle to the symbol shape.
#[derive(Hash)]
pub struct MergedSymbols<V, I, P, S = SymbolVisibility>(
  PhantomData<(V, I, P, S)>,
);

impl<V, I, P, S> Default for MergedSymbols<V, I, P, S> {
  fn default() -> Self {
    Self(PhantomData)
  }
}

impl<V, I, P, S> Clone for MergedSymbols<V, I, P, S> {
  fn clone(&self) -> Self {
    *self
  }
}

impl<V, I, P, S> Copy for MergedSymbols<V, I, P, S> {}

impl<V, I, P, S> std::fmt::Debug for MergedSymbols<V, I, P, S> {
  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
    f.debug_struct("MergedSymbols").finish()
  }
}

impl<V, I, P, S> PartitionKey for MergedSymbols<V, I, P, S>
where
  V: Value<I>,
  I: Ident,
  P: SymbolPath,
  S: Visibility,
{
  const KEY: LaburnumIdent = LaburnumIdent::new("symbolique::merged_symbols");
}

impl<V, I, P, S> Partition for MergedSymbols<V, I, P, S>
where
  V: Value<I>,
  I: Ident,
  P: SymbolPath,
  S: Visibility,
{
  type Record = (); // Index-only
  type IndexEntry = SymbolEntry<V, I, P, S>;
  type SortKey = P;
}

/// Merged-level symbol index partition.
#[derive(Hash)]
pub struct MergedSymbolIndex<V, I, P, S = SymbolVisibility>(
  PhantomData<(V, I, P, S)>,
);

impl<V, I, P, S> Default for MergedSymbolIndex<V, I, P, S> {
  fn default() -> Self {
    Self(PhantomData)
  }
}

impl<V, I, P, S> Clone for MergedSymbolIndex<V, I, P, S> {
  fn clone(&self) -> Self {
    *self
  }
}

impl<V, I, P, S> Copy for MergedSymbolIndex<V, I, P, S> {}

impl<V, I, P, S> std::fmt::Debug for MergedSymbolIndex<V, I, P, S> {
  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
    f.debug_struct("MergedSymbolIndex").finish()
  }
}

impl<V, I, P, S> PartitionKey for MergedSymbolIndex<V, I, P, S>
where
  V: Value<I>,
  I: Ident,
  P: SymbolPath,
  S: Visibility,
{
  const KEY: LaburnumIdent =
    LaburnumIdent::new("symbolique::merged_symbol_index");
}

impl<V, I, P, S> Partition for MergedSymbolIndex<V, I, P, S>
where
  V: Value<I>,
  I: Ident,
  P: SymbolPath,
  S: Visibility,
{
  type Record = (); // Index-only
  type IndexEntry = SymbolEntry<V, I, P, S>;
  type SortKey = P;
}

#[cfg(test)]
mod tests {
  use super::*;

  #[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
  struct TestPath(String);

  impl laburnum::PartitionSortKey for TestPath {
    fn is_prefix_of(&self, other: &Self) -> bool {
      other.0.starts_with(&self.0)
    }

    fn resolve(&self, _resolver: &dyn laburnum::SourceResolver) -> String {
      self.0.clone()
    }
  }

  #[test]
  fn merged_symbols_partition_key() {
    use crate::core::{DefaultValue, StringIdent};

    let key = MergedSymbols::<DefaultValue, StringIdent, TestPath>::KEY;
    let key2 = MergedSymbolIndex::<DefaultValue, StringIdent, TestPath>::KEY;
    assert_ne!(key, key2);
  }

  #[test]
  fn merged_symbols_is_index_only() {
    use crate::core::{DefaultValue, StringIdent};

    fn assert_record_is_unit<P: Partition<Record = ()>>() {}

    assert_record_is_unit::<MergedSymbols<DefaultValue, StringIdent, TestPath>>(
    );
  }
}
