// ========= Copyright 2026 @ Strukto.AI 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.
// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========

import type { Accessor } from '../../../accessor/base.ts'
import type { GitHubAccessor } from '../../../accessor/github.ts'
import type { IndexCacheStore } from '../../../cache/index/store.ts'
import { Precision, ProvisionResult } from '../../../provision/types.ts'
import type { PathSpec } from '../../../types.ts'
import type { CommandOpts } from '../../config.ts'
import { stat as githubStat } from '../../../core/github/stat.ts'

interface ResolvedSizes {
  resolved: [string, number][]
  missing: number
}

async function resolveSizes(
  accessor: GitHubAccessor,
  paths: readonly PathSpec[],
  index: IndexCacheStore | undefined,
): Promise<ResolvedSizes> {
  const resolved: [string, number][] = []
  let missing = 0
  for (const p of paths) {
    const pathStr = p.original
    let size: number | null = null
    if (index !== undefined) {
      const lookup = await index.get(pathStr)
      if (lookup.entry !== undefined && lookup.entry !== null) {
        size = lookup.entry.size
      }
    }
    if (size === null) {
      try {
        const fileStat = await githubStat(accessor, p, index)
        size = fileStat.size
      } catch {
        // size stays null
      }
    }
    if (size !== null) {
      resolved.push([pathStr, size])
    } else {
      missing += 1
    }
  }
  return { resolved, missing }
}

function parseNumFlag(value: string | boolean | undefined): number | null {
  if (typeof value !== 'string') return null
  const n = Number.parseInt(value, 10)
  return Number.isFinite(n) ? n : null
}

export async function fileReadProvision(
  accessor: Accessor,
  paths: PathSpec[],
  _texts: string[],
  opts: CommandOpts,
): Promise<ProvisionResult> {
  if (paths.length === 0) {
    return new ProvisionResult({ precision: Precision.UNKNOWN })
  }
  const index = opts.index ?? undefined
  const { resolved, missing } = await resolveSizes(accessor as GitHubAccessor, paths, index)
  if (missing > 0 || resolved.length === 0) {
    return new ProvisionResult({ precision: Precision.UNKNOWN })
  }
  const total = resolved.reduce((sum, [, size]) => sum + size, 0)
  return new ProvisionResult({
    networkReadLow: total,
    networkReadHigh: total,
    readOps: resolved.length,
    precision: Precision.EXACT,
  })
}

export async function headTailProvision(
  accessor: Accessor,
  paths: PathSpec[],
  _texts: string[],
  opts: CommandOpts,
): Promise<ProvisionResult> {
  if (paths.length === 0) {
    return new ProvisionResult({ precision: Precision.UNKNOWN })
  }
  const index = opts.index ?? undefined
  const { resolved, missing } = await resolveSizes(accessor as GitHubAccessor, paths, index)
  if (missing > 0 || resolved.length === 0) {
    return new ProvisionResult({ precision: Precision.UNKNOWN })
  }
  const c = parseNumFlag(opts.flags.c)
  if (c !== null) {
    const total = resolved.reduce((sum, [, size]) => sum + Math.min(c, size), 0)
    return new ProvisionResult({
      networkReadLow: total,
      networkReadHigh: total,
      readOps: resolved.length,
      precision: Precision.EXACT,
    })
  }
  const full = resolved.reduce((sum, [, size]) => sum + size, 0)
  return new ProvisionResult({
    networkReadLow: 0,
    networkReadHigh: full,
    readOps: resolved.length,
    precision: Precision.RANGE,
  })
}

export function metadataProvision(
  _accessor: Accessor,
  paths: PathSpec[],
  _texts: string[],
  _opts: CommandOpts,
): ProvisionResult {
  const n = Math.max(1, paths.length > 0 ? paths.length : 1)
  return new ProvisionResult({
    networkReadLow: 0,
    networkReadHigh: 0,
    readOps: n,
    precision: Precision.EXACT,
  })
}
