/*
 * Copyright (c) 2022-2025 Huawei Device Co., Ltd.
 * 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.
 */

import * as arkts from '@koalaui/libarkts';
import { Plugins, PluginContext } from '../common/plugin-context';
import { FunctionTransformer } from './function-transformer';
import { PositionalIdTracker } from './utils';
import { ReturnTransformer } from './return-transformer';
import { ParameterTransformer } from './parameter-transformer';
import { ProgramVisitor } from '../common/program-visitor';
import { EXTERNAL_SOURCE_PREFIX_NAMES, EXTERNAL_SOURCE_PREFIX_NAMES_FOR_FRAMEWORK, NodeCacheNames } from '../common/predefines';
import { Debugger, debugLog } from '../common/debug';
import { SignatureTransformer } from './signature-transformer';
import { InternalsTransformer } from './internal-transformer';
import { ProgramSkipper } from "../common/program-skipper";
import { NodeCacheFactory } from '../common/node-cache';
import { MetaDataCollector } from '../common/metadata-collector';

export function unmemoizeTransform(): Plugins {
    return {
        name: 'memo-plugin',
        checked: checkedTransform,
        clean() {
            ProgramSkipper.clear();
            PositionalIdTracker.clear();
            NodeCacheFactory.getInstance().clear();
        },
    };
}

function checkedTransform(this: PluginContext): arkts.ETSModule | undefined {
    Debugger.getInstance().phasesDebugLog('[MEMO PLUGIN] AFTER CHECKED ENTER');
    arkts.Performance.getInstance().memoryTrackerReset();
    arkts.Performance.getInstance().startMemRecord('Node:UIPlugin:Memo-AfterCheck');
    const contextPtr = this.getContextPtr() ?? arkts.arktsGlobal.compilerContext?.peer;
    const isCoding = this.isCoding?.() ?? false;
    if (!isCoding && !!contextPtr) {
        let program = arkts.getOrUpdateGlobalContext(contextPtr).program;
        let script = program.ast as arkts.ETSModule;
        debugLog('[BEFORE MEMO SCRIPT] script: ', script.dumpSrc());
        const cachePath: string | undefined = this.getProjectConfig()?.cachePath;
        const isFrameworkMode = !!this.getProjectConfig()?.frameworkMode;
        arkts.Performance.getInstance().createEvent('memo-checked');
        program = checkedProgramVisit(program, this, false, isFrameworkMode);
        script = program.ast as arkts.ETSModule;
        arkts.Performance.getInstance().stopEvent('memo-checked', true);
        debugLog('[AFTER MEMO SCRIPT] script: ', script.dumpSrc());

        arkts.Performance.getInstance().memoryTrackerGetDelta('UIPlugin:Memo-AfterCheck');
        arkts.Performance.getInstance().memoryTrackerReset();
        arkts.Performance.getInstance().stopMemRecord('Node:UIPlugin:Memo-AfterCheck');
        arkts.Performance.getInstance().startMemRecord('Node:ArkTS:Recheck');
        arkts.Performance.getInstance().createEvent('memo-recheck');
        arkts.recheckSubtree(script);
        arkts.Performance.getInstance().stopEvent('memo-recheck', true);
        this.setArkTSAst(script);
        arkts.Performance.getInstance().memoryTrackerGetDelta('ArkTS:Recheck');
        arkts.Performance.getInstance().stopMemRecord('Node:ArkTS:Recheck');
        arkts.Performance.getInstance().memoryTrackerPrintCurrent('UIPlugin:End');
        Debugger.getInstance().phasesDebugLog('[MEMO PLUGIN] AFTER CHECKED EXIT');
        return script;
    }
    Debugger.getInstance().phasesDebugLog('[MEMO PLUGIN] AFTER CHECKED EXIT WITH NO TRANSFORM');
    return undefined;
}

function checkedProgramVisit(
    program: arkts.Program,
    pluginContext: PluginContext,
    canSkipPhases: boolean = false,
    isFrameworkMode: boolean = false
): arkts.Program {
    if (canSkipPhases) {
        debugLog('[SKIP PHASE] phase: memo-checked, moduleName: ', program.moduleName);
    } else {
        debugLog('[CANT SKIP PHASE] phase: memo-checked, moduleName: ', program.moduleName);
        // NodeCacheFactory.getInstance().getCache(NodeCacheNames.MEMO).visualize();
        const positionalIdTracker = new PositionalIdTracker(arkts.getFileName(), false);
        const parameterTransformer = new ParameterTransformer({ positionalIdTracker });
        const returnTransformer = new ReturnTransformer();
        const signatureTransformer = new SignatureTransformer();
        let internalsTransformer: InternalsTransformer | undefined;
        if (isFrameworkMode) {
            internalsTransformer = new InternalsTransformer({ positionalIdTracker });
        }
        const functionTransformer = new FunctionTransformer({
            positionalIdTracker,
            parameterTransformer,
            returnTransformer,
            signatureTransformer,
            internalsTransformer,
            useCache: NodeCacheFactory.getInstance().getCache(NodeCacheNames.MEMO).isCollected(),
        });
        const skipPrefixNames = isFrameworkMode
            ? EXTERNAL_SOURCE_PREFIX_NAMES_FOR_FRAMEWORK
            : EXTERNAL_SOURCE_PREFIX_NAMES;
        const programVisitor = new ProgramVisitor({
            pluginName: unmemoizeTransform.name,
            state: arkts.Es2pandaContextState.ES2PANDA_STATE_CHECKED,
            visitors: [functionTransformer],
            skipPrefixNames,
            pluginContext,
            isFrameworkMode
        });
        program = programVisitor.programVisitor(program);
        MetaDataCollector.getInstance().reset();
        NodeCacheFactory.getInstance().getCache(NodeCacheNames.MEMO).clear();
    }
    return program;
}