// Copyright (c) Meta Platforms, Inc. and affiliates.
//
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.

//! Converts `MethodCall` instructions on props objects into `CallExpression`
//! instructions.
//!
//! When the receiver of a method call is typed as the component's props object,
//! we can safely convert the method call `props.foo(args)` into a direct call
//! `foo(args)` using the property as the callee. This simplifies downstream
//! analysis by removing the receiver dependency.
//!
//! Analogous to TS `Optimization/OptimizePropsMethodCalls.ts`.

use std::mem::replace;

use crate::react_compiler_hir::environment::Environment;
use crate::react_compiler_hir::{HirFunction, InstructionValue, is_props_type};

pub fn optimize_props_method_calls(func: &mut HirFunction, env: &Environment) {
    for (_block_id, block) in &func.body.blocks {
        let instruction_ids: Vec<_> = block.instructions.clone();
        for instr_id in instruction_ids {
            let instr = &mut func.instructions[instr_id.index()];
            let should_replace = matches!(
                &instr.value,
                InstructionValue::MethodCall { receiver, .. }
                    if {
                        let identifier = &env.identifiers[receiver.identifier];
                        let ty = &env.types[identifier.type_];
                        is_props_type(ty)
                    }
            );
            if should_replace {
                // Take the old value out, replacing with a temporary.
                // The if-let is guaranteed to match since we checked above.
                let old = replace(&mut instr.value, InstructionValue::Debugger { span: None });
                match old {
                    InstructionValue::MethodCall { property, args, span, .. } => {
                        instr.value =
                            InstructionValue::CallExpression { callee: property, args, span };
                    }
                    _ => unreachable!(),
                }
            }
        }
    }
}
