/*
 * Copyright (c) 2026 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 { Effect, Schema } from "effect"
import * as Tool from "./tool"
import { findDevEcoHome, hdcPath } from "./lib/env"
import DESCRIPTION from "./hdc-log.txt"

function pick(input: string, prefix: string, lines: number) {
  const list = input
    .split(/\r?\n/)
    .map((item) => item.trim())
    .filter(Boolean)
  const filtered = prefix ? list.filter((item) => item.includes(prefix)) : list
  return filtered.slice(Math.max(0, filtered.length - lines))
}

async function run(cmd: string[]) {
  const proc = Bun.spawn({
    cmd,
    stdout: "pipe",
    stderr: "pipe",
  })
  const [stdout, stderr, exitCode] = await Promise.all([
    proc.stdout ? Bun.readableStreamToText(proc.stdout) : Promise.resolve(""),
    proc.stderr ? Bun.readableStreamToText(proc.stderr) : Promise.resolve(""),
    proc.exited,
  ])
  return { stdout, stderr, exitCode }
}

function target(device: string | undefined) {
  return device ? ["-t", device] : []
}

interface HdcLogMetadata {
  deviceCount?: number
  lineCount?: number
}

const Parameters = Schema.Struct({
  action: Schema.Literals(["collect", "clear", "list_devices"])
    .annotate({ description: "Action to perform" }),
  device_id: Schema.optional(Schema.String)
    .annotate({ description: "Optional hdc target id" }),
  log_prefix: Schema.String
    .pipe(Schema.optional, Schema.withDecodingDefault(Effect.succeed("[VCODER_DEBUG]")))
    .annotate({ description: "Log prefix to filter" }),
  lines: Schema.Int
    .check(Schema.isGreaterThanOrEqualTo(1), Schema.isLessThanOrEqualTo(5000))
    .pipe(Schema.optional, Schema.withDecodingDefault(Effect.succeed(2000)))
    .annotate({ description: "Number of log lines to collect" }),
})

export const HdcLogTool = Tool.define("hdc_log", Effect.gen(function* () {
  return {
    description: DESCRIPTION,
    parameters: Parameters,
    execute: (args: Schema.Schema.Type<typeof Parameters>, _ctx: Tool.Context<HdcLogMetadata>) =>
      Effect.gen(function* () {
        const home = yield* Effect.tryPromise(() => findDevEcoHome())
        if (!home) {
          throw new Error("DevEco Studio path not found. Set DEVECO_HOME and retry.")
        }
        const hdc = hdcPath(home)
        const hdcExists = yield* Effect.tryPromise(() => Bun.file(hdc).exists())
        if (!hdcExists) {
          throw new Error(`hdc not found: ${hdc}`)
        }

        if (args.action === "list_devices") {
          const out = yield* Effect.tryPromise(() => run([hdc, "list", "targets"]))
          if (out.exitCode !== 0) {
            throw new Error(`hdc list targets failed (code=${out.exitCode}): ${out.stderr || out.stdout}`)
          }
          const devices = out.stdout
            .split(/\r?\n/)
            .map((item) => item.trim())
            .filter((item) => item && !item.includes("[Empty]"))
          if (!devices.length) {
            return {
              title: "No Devices",
              output: "No connected devices detected.",
              metadata: { deviceCount: 0, lineCount: undefined } as HdcLogMetadata,
            }
          }
          return {
            title: "Connected Devices",
            output: ["Connected devices:", ...devices.map((item, i) => `${i + 1}. ${item}`)].join("\n"),
            metadata: { deviceCount: devices.length, lineCount: undefined } as HdcLogMetadata,
          }
        }

        if (args.action === "clear") {
          const out = yield* Effect.tryPromise(() => run([hdc, ...target(args.device_id), "shell", "hilog", "-r"]))
          if (out.exitCode !== 0) {
            throw new Error(`hdc hilog -r failed (code=${out.exitCode}): ${out.stderr || out.stdout}`)
          }
          return {
            title: "Log Buffer Cleared",
            output: ["Device log buffer cleared.", `device: ${args.device_id || "default"}`].join("\n"),
            metadata: { deviceCount: undefined, lineCount: undefined } as HdcLogMetadata,
          }
        }

        const out = yield* Effect.tryPromise(() => run([hdc, ...target(args.device_id), "shell", "hilog", "-x"]))
        if (out.exitCode !== 0) {
          throw new Error(`hdc hilog -x failed (code=${out.exitCode}): ${out.stderr || out.stdout}`)
        }
        const logs = pick(out.stdout, args.log_prefix ?? "", args.lines ?? 2000)
        if (!logs.length) {
          return {
            title: "No Matching Logs",
            output: [
              "No matching logs found.",
              `device: ${args.device_id || "default"}`,
              `prefix: ${args.log_prefix}`,
            ].join("\n"),
            metadata: { deviceCount: undefined, lineCount: 0 } as HdcLogMetadata,
          }
        }
        return {
          title: "Log Collection Successful",
          output: [
            "Log collection successful.",
            `device: ${args.device_id || "default"}`,
            `prefix: ${args.log_prefix}`,
            `count: ${logs.length}`,
            "",
            "--- Log Content ---",
            ...logs,
          ].join("\n"),
          metadata: { deviceCount: undefined, lineCount: logs.length } as HdcLogMetadata,
        }
      }).pipe(Effect.orDie),
  }
}))