//
// Copyright 2025 Formata, Inc. All rights reserved.
//
// Licensed 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.
//

use std::sync::Arc;
use arcstr::literal;
use imbl::vector;
use nom::{branch::alt, bytes::complete::tag, character::complete::{char, multispace0, one_of}, combinator::{opt, peek, recognize}, multi::{many1, separated_list0, separated_list1}, sequence::{delimited, preceded, separated_pair, terminated}, IResult, Parser};
use crate::{model::SId, parser::{doc::StofParseError, expr::{fmt_str::formatted_string_expr, func::func_expr, graph::{call_expr, chained_var_func, graph_expr}, literal::literal_expr, math::math_expr, new_obj::new_obj_expr}, statement::{block, switch::switch_statement}, types::parse_type, whitespace::whitespace}, runtime::{instruction::{Instruction, Instructions}, instructions::{block::Block, call::FuncCall, ifs::IfIns, list::{ListIns, NEW_LIST}, map::{MapIns, NEW_MAP}, nullcheck::NullcheckIns, set::{SetIns, NEW_SET}, tup::{TupIns, NEW_TUP}, Base, AWAIT, NOOP, NOT_TRUTHY, POP_RETURN, PUSH_RETURN, SUSPEND, TYPE_NAME, TYPE_OF}, Type, Val}};

pub mod literal;
pub mod math;
pub mod graph;
pub mod func;
pub mod fmt_str;
pub mod new_obj;


/// Parse an expression.
pub fn expr(input: &str) -> IResult<&str, Arc<dyn Instruction>, StofParseError> {
    let (input, mut ins) = alt([
        new_obj_expr,
        func_expr,
        await_expr,
        async_expr,
        typename_expr,
        typeof_expr,
        tup_expr,
        list_expr,
        blob_expr,
        set_expr,
        map_expr,
        math_expr,
        not_expr,
        block_expr,
        switch_expr,
        literal_expr,
        formatted_string_expr,
        graph_expr,
        wrapped_expr,
    ]).parse(input)?;

    // Peek at the next value, if its async, then don't do the as below...
    let (mut input, peek_async) = opt(peek(preceded(multispace0, tag("async")))).parse(input)?;
    if peek_async.is_none() {
        // Optional "as Type" cast before other operators
        let (inner_input, cast_type) = opt(preceded(delimited(multispace0, tag("as"), multispace0), parse_type)).parse(input)?;
        if let Some(cast) = cast_type {
            let mut block = Block::default();
            block.ins.push_back(ins);
            block.ins.push_back(Arc::new(Base::Cast(cast)));
            
            input = inner_input;
            ins = Arc::new(block);
        }
    }

    // Nullcheck operator "??"
    let (input, nullcheck) = opt(preceded(delimited(multispace0, tag("??"), multispace0), expr)).parse(input)?;
    if let Some(nullcheck) = nullcheck {
        ins = Arc::new(NullcheckIns {
            ins,
            ifnull: nullcheck
        });
    }

    // Ternary operator "?"
    let (input, ternary) = opt(ternary_operator).parse(input)?;
    if let Some((if_expr, else_expr)) = ternary {
        ins = Arc::new(IfIns {
            if_test: Some(ins),
            if_ins: vector![if_expr],
            el_ins: vector![else_expr],
        });
    }

    Ok((input, ins))
}


/// Ternary operator.
/// test_expr ? if_expr : else_expr
fn ternary_operator(input: &str) -> IResult<&str, (Arc<dyn Instruction>, Arc<dyn Instruction>), StofParseError> {
    let (input, _) = delimited(multispace0, char('?'), multispace0).parse(input)?;
    let (input, if_expr) = terminated(expr, delimited(multispace0, char(':'), multispace0)).parse(input)?;
    let (input, else_expr) = expr(input)?;
    Ok((input, (if_expr, else_expr)))
}


/// Block expression.
pub fn block_expr(input: &str) -> IResult<&str, Arc<dyn Instruction>, StofParseError> {
    let (input, mut statements) = block(input)?;
    if statements.is_empty() { return Ok((input, NOOP.clone())); }

    // return statements within a block expression return at the block, not the function, so spoof it
    let tmp = SId::default();
    statements.push_front(PUSH_RETURN.clone());
    statements.push_front(Arc::new(Base::Literal(Val::Fn(tmp.clone()))));

    statements.push_back(Arc::new(Base::Tag(tmp.as_ref().into())));
    statements.push_back(POP_RETURN.clone());

    Ok((input, Arc::new(Block { ins: statements })))
}


/// Switch expression.
pub fn switch_expr(input: &str) -> IResult<&str, Arc<dyn Instruction>, StofParseError> {
    let (input, mut statements) = switch_statement(input)?;
    if statements.is_empty() { return Ok((input, NOOP.clone())); }

    // return statements within a block expression return at the block, not the function, so spoof it
    let tmp = SId::default();
    statements.push_front(PUSH_RETURN.clone());
    statements.push_front(Arc::new(Base::Literal(Val::Fn(tmp.clone()))));

    statements.push_back(Arc::new(Base::Tag(tmp.as_ref().into())));
    statements.push_back(POP_RETURN.clone());

    Ok((input, Arc::new(Block { ins: statements })))
}


/// Blob expression.
pub fn blob_expr(input: &str) -> IResult<&str, Arc<dyn Instruction>, StofParseError> {
    let (input, _) = whitespace(input)?;
    let (input, bytes) = delimited(
        char('|'),
        terminated(separated_list1(char(','), blob_number), whitespace),
        alt((
            preceded(char(','), preceded(whitespace, char('|'))),
            char('|')
        ))
    ).parse(input)?;
    Ok((input, Arc::new(Base::Literal(Val::Blob(bytes.into())))))
}
pub fn blob_number(input: &str) -> IResult<&str, u8, StofParseError> {
    let (input, _) = whitespace(input)?;
    let (input, recognized) = recognize(many1(one_of("0123456789"))).parse(input)?;
    let value = recognized.parse::<u8>().expect("could not parse floating point number");
    Ok((input, value))
}


/// List contructor expression.
pub fn list_expr(input: &str) -> IResult<&str, Arc<dyn Instruction>, StofParseError> {
    let (input, _) = whitespace(input)?;
    let (input, exprs) = delimited(
        char('['),
        terminated(separated_list0(char(','), expr), whitespace),
        alt((
                preceded(char(','), preceded(whitespace, char(']'))),
                char(']')
            ))
    ).parse(input)?;

    // Optional chained calls here
    // Ex. [3, 4].at(0)
    let (input, additional) = opt(preceded(char('.'), separated_list1(char('.'), chained_var_func))).parse(input)?;

    let mut block = Block::default();
    block.ins.push_back(NEW_LIST.clone());
    for expr in exprs {
        block.ins.push_back(Arc::new(ListIns::AppendList(expr)));
    }
    if let Some(additional) = additional {
        for ins in additional {
            block.ins.push_back(ins);
        }
    }

    Ok((input, Arc::new(block)))
}


/// Tuple contructor expression.
pub fn tup_expr(input: &str) -> IResult<&str, Arc<dyn Instruction>, StofParseError> {
    let (input, _) = whitespace(input)?;
    let (input, exprs) = delimited(
        char('('),
        terminated(separated_list1(char(','), expr), whitespace),
        alt((
                preceded(char(','), preceded(whitespace, char(')'))),
                char(')')
            ))
    ).parse(input)?;

    if exprs.len() < 2 {
        return Err(nom::Err::Error(StofParseError::from(format!("tuple constructor requires at least 2 values"))));
    }

    // Optional chained calls here
    // Ex. (3, 4).at(0)
    let (input, additional) = opt(preceded(char('.'), separated_list1(char('.'), chained_var_func))).parse(input)?;

    let mut block = Block::default();
    block.ins.push_back(NEW_TUP.clone());
    for expr in exprs {
        block.ins.push_back(Arc::new(TupIns::AppendTup(expr)));
    }
    if let Some(additional) = additional {
        for ins in additional {
            block.ins.push_back(ins);
        }
    }

    Ok((input, Arc::new(block)))
}


/// Set contructor expression.
pub fn set_expr(input: &str) -> IResult<&str, Arc<dyn Instruction>, StofParseError> {
    let (input, _) = whitespace(input)?;
    let (input, exprs) = delimited(
        char('{'),
        terminated(separated_list0(char(','), expr), whitespace),
        alt((
                preceded(char(','), preceded(whitespace, char('}'))),
                char('}')
            ))
    ).parse(input)?;

    // Optional chained calls here
    // Ex. {3, 4}.at(0)
    let (input, additional) = opt(preceded(char('.'), separated_list1(char('.'), chained_var_func))).parse(input)?;

    let mut block = Block::default();
    block.ins.push_back(NEW_SET.clone());
    for expr in exprs {
        block.ins.push_back(Arc::new(SetIns::AppendSet(expr)));
    }
    if let Some(additional) = additional {
        for ins in additional {
            block.ins.push_back(ins);
        }
    }

    Ok((input, Arc::new(block)))
}


/// Map contructor expression.
pub fn map_expr(input: &str) -> IResult<&str, Arc<dyn Instruction>, StofParseError> {
    let (input, _) = whitespace(input)?;
    let (input, exprs) = delimited(
        char('{'),
        terminated(separated_list0(char(','), separated_pair(expr, char(':'), expr)), whitespace),
        alt((
                preceded(char(','), preceded(whitespace, char('}'))),
                char('}')
            ))
    ).parse(input)?;

    // Optional chained calls here
    // Ex. {'a': 3, 'b': 4}.at('b')
    let (input, additional) = opt(preceded(char('.'), separated_list1(char('.'), chained_var_func))).parse(input)?;

    let mut block = Block::default();
    block.ins.push_back(NEW_MAP.clone());
    for expr in exprs {
        block.ins.push_back(Arc::new(MapIns::AppendMap(expr)));
    }
    if let Some(additional) = additional {
        for ins in additional {
            block.ins.push_back(ins);
        }
    }

    Ok((input, Arc::new(block)))
}


/// Await expression.
pub fn await_expr(input: &str) -> IResult<&str, Arc<dyn Instruction>, StofParseError> {
    let (input, _) = whitespace(input)?;
    let (input, ins) = preceded(tag("await"), expr).parse(input)?;
    
    let mut block = Block::default();
    block.ins.push_back(ins); // a promise (maybe)
    block.ins.push_back(AWAIT.clone()); // will only do something if its a promise
    
    Ok((input, Arc::new(block)))
}


/// Async expression.
/// Spawns a new process with an expr.
pub fn async_expr(input: &str) -> IResult<&str, Arc<dyn Instruction>, StofParseError> {
    let (input, _) = whitespace(input)?;
    let (input, ins) = preceded(tag("async"), expr).parse(input)?;

    // Spawn will put a new Promise<unknown> on the stack for you use if you want
    let mut block = Block::default();
    block.ins.push_back(Arc::new(Base::Spawn((Instructions::from(ins), Type::Void)))); // no casting by default
    block.ins.push_back(SUSPEND.clone()); // start the new process right away

    Ok((input, Arc::new(block)))
}


/// Wrapped expression.
pub fn wrapped_expr(input: &str) -> IResult<&str, Arc<dyn Instruction>, StofParseError> {
    let (input, _) = whitespace(input)?;
    let (input, mut ins) = delimited(char('('), delimited(multispace0, expr, multispace0), char(')')).parse(input)?;
    let (mut input, additional) = opt(preceded(char('.'), separated_list1(char('.'), chained_var_func))).parse(input)?;

    if additional.is_some() {
        let mut block = Block::default();
        block.ins.push_back(ins);
        if let Some(additional) = additional {
            for ins in additional {
                block.ins.push_back(ins);
            }
        }
        ins = Arc::new(block);
    } else {
        let (rest, call_args) = opt(call_expr).parse(input)?;
        if call_args.is_some() {
            input = rest;

            let mut block = Block::default();
            block.ins.push_back(ins);
            block.ins.push_back(Arc::new(FuncCall {
                as_ref: false,
                cnull: false,
                func: None,
                search: Some(literal!("")),
                stack: true,
                args: call_args.unwrap().into_iter().collect(),
                oself: None,
            }));

            ins = Arc::new(block);
        }
    }

    Ok((input, ins))
}


/// Not expression.
pub fn not_expr(input: &str) -> IResult<&str, Arc<dyn Instruction>, StofParseError> {
    let (input, _) = whitespace(input)?;
    let (input, ins) = preceded(char('!'), expr).parse(input)?;
    
    let mut block = Block::default();
    block.ins.push_back(ins);
    block.ins.push_back(NOT_TRUTHY.clone());
    
    Ok((input, Arc::new(block)))
}


/// TypeOf expression.
pub fn typeof_expr(input: &str) -> IResult<&str, Arc<dyn Instruction>, StofParseError> {
    let (input, _) = whitespace(input)?;
    let (input, ins) = preceded(tag("typeof"), expr).parse(input)?;

    let mut block = Block::default();
    block.ins.push_back(ins);
    block.ins.push_back(TYPE_OF.clone());
    
    Ok((input, Arc::new(block)))
}


/// TypeName expression.
/// A specific type instead of a general one Ex. "MyObj" instead of "obj"
pub fn typename_expr(input: &str) -> IResult<&str, Arc<dyn Instruction>, StofParseError> {
    let (input, _) = whitespace(input)?;
    let (input, ins) = preceded(tag("typename"), expr).parse(input)?;

    let mut block = Block::default();
    block.ins.push_back(ins);
    block.ins.push_back(TYPE_NAME.clone());
    
    Ok((input, Arc::new(block)))
}


#[cfg(test)]
mod tests {
    use crate::{model::Graph, parser::expr::expr, runtime::Runtime};

    #[test]
    fn parse_map_expr() {
        let (_input, res) = expr("{'a': 1, 'b': 2, 'c': 3}").unwrap();
        let mut graph = Graph::default();
        let res = Runtime::eval(&mut graph, res).unwrap();
        //println!("{}", res.print(&graph));
        assert!(res.map());
    }

    #[test]
    fn parse_list_expr() {
        let (_input, res) = expr("['a', 2, 'c']").unwrap();
        let mut graph = Graph::default();
        let res = Runtime::eval(&mut graph, res).unwrap();
        //println!("{}", res.print(&graph));
        assert!(res.list());
    }

    #[test]
    fn parse_tup_expr() {
        let (_input, res) = expr("('a', 2, 'c')").unwrap();
        let mut graph = Graph::default();
        let res = Runtime::eval(&mut graph, res).unwrap();
        //println!("{}", res.print(&graph));
        assert!(res.tup());
    }

    #[test]
    fn parse_set_expr() {
        let (_input, res) = expr("{'a', 2, 'c'}").unwrap();
        let mut graph = Graph::default();
        let res = Runtime::eval(&mut graph, res).unwrap();
        //println!("{}", res.print(&graph));
        assert!(res.set());
    }

    #[test]
    fn parse_wrapped_expr() {
        let (_input, res) = expr("(['a', 2, 'c'])").unwrap();
        let mut graph = Graph::default();
        let res = Runtime::eval(&mut graph, res).unwrap();
        //println!("{}", res.print(&graph));
        assert!(res.list());
    }

    #[test]
    fn await_passthrough_expr() {
        let (_input, res) = expr("await 42").unwrap();
        let mut graph = Graph::default();
        let res = Runtime::eval(&mut graph, res).unwrap();
        assert_eq!(res, 42.into());
    }
}
