/*
 * Copyright (c) 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 vscode from 'vscode';
import { buildCjmpProject } from './keelsBuild';
import { cleanCjmpProject } from './keelsClean';
import { disposeCjmpOutputChannel } from './cjmpOutputChannel';
import { createCjmpProject, executeSDK } from './keelsCreate';
import { LaunchEmulator } from './keelsEmulators';
import { KeelsDebugAdapterDescriptorFactory, KeelsConfigurationProvider } from './keelsRun';
import {
  getAdbPath,
  getPackageName,
  getProjectType,
  KeelsDeviceManager,
  TimeDiff,
  activateStripAnsi,
  configDefaultEnvToSettings,
  configureCjpmTomlPath
} from './utils';
import { execNativeCommand } from './commandExecutor';
import path from 'path';
import * as fs from 'fs';
import { DebugSession } from 'vscode';
import { RemotePlatform } from './types';
import {
  cangjieBuildContextKey,
  cjmpProjectContextKey,
  getCjmpProjectWorkspaceFolder,
} from './cjmpProjectContext';

export async function activate(context: vscode.ExtensionContext) {
  console.log('Extension "CJMP" is now active!');
  activateStripAnsi();

  // Detect the initial CJMP project state and expose it to menu visibility conditions.
  const isCjmpProject = await updateProjectContexts();

  // Keep the device status bar hidden when the extension is activated outside a CJMP project.
  const deviceManager = new KeelsDeviceManager(context, isCjmpProject);

  const createCommand = vscode.commands.registerCommand('cjmp.createCjmpProject', async () => {
    await createCjmpProject(context);
  });

  const launchCommand = vscode.commands.registerCommand('cjmp.launchEmulator', async () => {
    await LaunchEmulator(context);
  });

  const buildCommand = vscode.commands.registerCommand('cjmp.build', async () => {
    const isSdkReady = await executeSDK(context);
    if (!isSdkReady) {
      return;
    }
    await buildCjmpProject();
  });

  const cleanCommand = vscode.commands.registerCommand('cjmp.clean', async () => {
    const isSdkReady = await executeSDK(context);
    if (!isSdkReady) {
      return;
    }
    await cleanCjmpProject();
  });

  context.subscriptions.push(
    vscode.commands.registerCommand(
      'cjmp.selectDevice',
      deviceManager.showDevicePicker,
      deviceManager,
    ),
  );

  const provider = new KeelsConfigurationProvider(deviceManager);
  context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider('keels', provider));

  const factory = new KeelsDebugAdapterDescriptorFactory(deviceManager, context);
  context.subscriptions.push(vscode.debug.registerDebugAdapterDescriptorFactory('keels', factory));

  // Dispose commands with the extension.
  context.subscriptions.push(buildCommand);
  context.subscriptions.push(cleanCommand);
  context.subscriptions.push(createCommand);
  context.subscriptions.push(launchCommand);
  context.subscriptions.push({ dispose: disposeCjmpOutputChannel });
  context.subscriptions.push(deviceManager);
  context.subscriptions.push(vscode.debug.onDidTerminateDebugSession((session) => sessionTerminated(session)));
  context.subscriptions.push(vscode.workspace.onDidChangeWorkspaceFolders(() => {
    void updateProjectContexts();
  }));

  // Project-specific files and settings are initialized only for a recognized CJMP workspace.
  if (isCjmpProject) {
    context.subscriptions.push(vscode.workspace.onDidChangeConfiguration((event) => {
      if (event.affectsConfiguration('CJMP.sdkPath')) {
        void configDefaultEnvToSettings();
      }
    }));
    await checkAndOpenTheDefaultFile();
    await configDefaultEnvToSettings();
    await configureCjpmTomlPath();
  }
}

async function updateProjectContexts(): Promise<boolean> {
  const isCjmpProject = getCjmpProjectWorkspaceFolder(vscode.workspace.workspaceFolders) !== undefined;
  await vscode.commands.executeCommand('setContext', cjmpProjectContextKey, isCjmpProject);
  // Cangjie shows its Build action when this context is true; hide it for CJMP projects.
  await vscode.commands.executeCommand('setContext', cangjieBuildContextKey, !isCjmpProject);
  return isCjmpProject;
}

async function sessionTerminated(session: DebugSession) {
  if (session.type !== 'cangjieDebug') {
    return;
  }
  const config = session.configuration;
  if (config.configPlatform !== 'keels') {
    return;
  }
  if (config.remotePlatform === RemotePlatform.ANDROID && config.deviceSerialNum && config.bundleName) {
    const adbPath = getAdbPath();
    if (!adbPath) {
      return;
    }
    const adbCmd = process.platform === 'win32' ? `"${adbPath}" -s ${config.deviceSerialNum}` : `${adbPath.replace(/ /g, '\\ ')} -s ${config.deviceSerialNum}`;
    await execNativeCommand(`${adbCmd} shell "run-as ${config.bundleName} sh -c 'pkill -9 lldb-server || true'"`);
  }
}

// This method is called when your extension is deactivated
export function deactivate() { }

async function checkAndOpenTheDefaultFile() {
  const workspaceFolders = vscode.workspace.workspaceFolders;
  if (!workspaceFolders) {
    return;
  }

  const projectPath = workspaceFolders[0].uri.fsPath;
  const lockPath = path.join(projectPath, '.vscode', '.CJMP_create.lock');

  if (fs.existsSync(lockPath)) {
    setTimeout(async () => {
      try {
        const timeDiff = Date.now() - parseInt(fs.readFileSync(lockPath, 'utf8').trim());
        if (timeDiff > TimeDiff) {
          fs.unlinkSync(lockPath);
          return;
        }
        const initialSourceFilePath = await resolveInitialSourceFilePath(projectPath);
        const document = await vscode.workspace.openTextDocument(
          vscode.Uri.file(initialSourceFilePath),
        );
        await vscode.window.showTextDocument(document);
        fs.unlinkSync(lockPath);
      } catch (error) {
        vscode.window.showErrorMessage(`Open initial Cangjie source file failed: ${error}`);
      }
    }, 300);
  }
}

export async function resolveInitialSourceFilePath(projectPath: string): Promise<string> {
  const projectType = getProjectType();
  switch (projectType) {
    case 'logic-module': {
      const packageName = await getPackageName();
      if (!packageName) {
        throw new Error('Package name is missing from project.conf.');
      }
      return path.join(projectPath, 'logic-module', 'common', `${packageName}.cj`);
    }
    case 'library': {
      const packageName = await getPackageName();
      if (!packageName) {
        throw new Error('Package name is missing from project.conf.');
      }
      return path.join(projectPath, 'lib', 'common', `${packageName}.cj`);
    }
    case 'app':
    case 'module':
      return path.join(projectPath, 'lib', 'common', 'main.cj');
    default:
      throw new Error(`Unsupported CJMP project type: ${projectType ?? 'unknown'}.`);
  }
}