// Copyright 2023-2024, Offchain Labs, Inc.
// For licensing, see https://github.com/OffchainLabs/stylus-sdk-rs/blob/main/licenses/COPYRIGHT.md

//! Traits for exporting Solidity interfaces.
//!
//! The contents of this module are imported when the `export-abi` feature flag is enabled,
//! which happens automatically during [`cargo stylus export-abi`][cargo].
//!
//! [cargo]: https://github.com/OffchainLabs/cargo-stylus#exporting-solidity-abis

use core::fmt;

use clap::{Parser, Subcommand};
use stylus_core;

#[doc(hidden)]
pub mod internal;

const DEFAULT_LICENSE: &str = "MIT-OR-APACHE-2.0";
const DEFAULT_PRAGMA: &str = "pragma solidity ^0.8.23;";

/// Export information about the Stylus contract.
#[derive(Parser)]
struct ExportCLI {
    #[command(subcommand)]
    commands: Option<ExportCommands>,
}

#[derive(Subcommand)]
enum ExportCommands {
    /// Export the Stylus contract ABI as a Solidity interface.
    Abi {
        /// Lisense of the generated ABI file.
        #[arg(long, default_value = DEFAULT_LICENSE)]
        license: String,
        /// Solidity pragma line on the generated ABI file.
        #[arg(long, default_value = DEFAULT_PRAGMA)]
        pragma: String,
    },
    /// Export the constructor signature.
    Constructor,
}

/// Prints the license and pragma given the CLI options.
pub fn handle_license_and_pragma() {
    let args = ExportCLI::parse();
    match args.commands {
        None => {
            print_license_and_pragma(DEFAULT_LICENSE, DEFAULT_PRAGMA);
        }
        Some(ExportCommands::Abi { license, pragma }) => {
            print_license_and_pragma(&license, &pragma);
        }
        _ => {}
    }
}

/// Prints the ABI given the CLI options.
pub fn print_from_args<T: GenerateAbi>() {
    let args = ExportCLI::parse();
    match args.commands {
        None => {
            print!("{}", AbiPrinter(T::fmt_abi));
        }
        Some(ExportCommands::Abi {
            license: _,
            pragma: _,
        }) => {
            print!("{}", AbiPrinter(T::fmt_abi));
        }
        Some(ExportCommands::Constructor) => {
            print_constructor_signature::<T>();
        }
    }
}

/// Trait for storage types so that users can print a Solidity interface to the console.
/// This is auto-derived via the [`public`] macro when the `export-abi` feature is enabled.
///
/// [`public`]: stylus-proc::public
pub trait GenerateAbi {
    /// The interface's name.
    const NAME: &'static str;

    /// How to format the ABI. Analogous to [`Display`](std::fmt::Display).
    fn fmt_abi(f: &mut fmt::Formatter<'_>) -> fmt::Result;

    /// How to format the constructor signature. Analogous to [`Display`](std::fmt::Display).
    fn fmt_constructor_signature(f: &mut fmt::Formatter<'_>) -> fmt::Result;
}

/// Type that makes an ABI printable.
struct AbiPrinter(fn(&mut fmt::Formatter<'_>) -> fmt::Result);

impl fmt::Display for AbiPrinter {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.0(f)
    }
}

/// Prints the license and pragma to the console.
pub fn print_license_and_pragma(license: &str, pragma: &str) {
    println!("/**");
    println!(" * This file was automatically generated by Stylus and represents a Rust program.");
    println!(" * For more information, please see [The Stylus SDK](https://github.com/OffchainLabs/stylus-sdk-rs).");
    println!(" */");
    println!();
    println!("// SPDX-License-Identifier: {license}");
    println!("{pragma}");
    println!();
}

fn print_constructor_signature<T: GenerateAbi>() {
    print!("{}", AbiPrinter(T::fmt_constructor_signature));
}

/// Prepends the string with an underscore if it is a Solidity keyword.
/// Otherwise, the string is unchanged.
/// Note: also prepends a space when the input is nonempty.
pub fn underscore_if_sol(name: &str) -> String {
    let underscore = || format!(" _{name}");

    if stylus_core::is_sol_keyword(name) {
        return underscore();
    }

    match name {
        "" => "".to_string(),
        _ => format!(" {name}"),
    }
}
