# ========= 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. =========

from dataclasses import dataclass

from mirage.commands.spec.compile import CompiledSpec


@dataclass(frozen=True, slots=True)
class OldStyleArgv:
    """An argv whose leading option-letter cluster has been dashed out.

    Args:
        argv (list[str]): the rewritten words.
        origins (list[int]): for each rewritten word, the position it came
            from in the caller's argv. Synthesized flag tokens all carry
            the cluster's own position, so a word kind written back
            through this table can only land on a real word.
        cluster (str | None): the word read as a letter cluster, when the
            line had one. The parser marks its slot TEXT so the word
            survives path classification verbatim and the dispatch-time
            scan reads the same letters the hint-time scan did.
        needs_value (str | None): the cluster letter whose argument ran
            off the end of the line, when one did.
    """
    argv: list[str]
    origins: list[int]
    cluster: str | None = None
    needs_value: str | None = None


def expand_old_style(cs: CompiledSpec, argv: list[str]) -> OldStyleArgv:
    """Rewrite GNU tar's old option style into ordinary dashed flags.

    tar's first word, when it carries no leading dash, is a cluster of
    option letters whose arguments follow it as separate words in letter
    order: ``tar xzCf dir a.tgz`` is ``-x -z -C dir -f a.tgz``. Only the
    first word reads this way, which is GNU's own rule (``tar -v czf
    a.tgz f`` is a usage error), so a line that already starts with a
    dash is returned untouched.

    Per letter, not one ``-xzCf`` token, because the getopt cluster rules
    cannot express what a tar cluster can: it may hold several
    argument-taking letters (``tar cvfb a.tar 20 f``) and it may continue
    past one (``tar cfz a.tgz f`` gzips, where getopt would read ``z`` as
    f's value). An argument is taken verbatim, dash or not, the way GNU
    does: ``tar xzf -C ex`` looks for an archive literally named ``-C``.

    An undeclared letter becomes an undeclared flag token and is refused
    by the parser's own scan, so this function only reports the one error
    getopt cannot see: a value letter with nothing left to consume.
    Pair options are not expanded here; no tool spells old style and a
    two-token option at once.

    Args:
        cs (CompiledSpec): compiled tables naming the value-taking shorts.
        argv (list[str]): the caller's words, without the command name.
    """
    if not argv or argv[0].startswith("-"):
        return OldStyleArgv(list(argv), list(range(len(argv))))
    cluster = argv[0]
    out: list[str] = []
    origins: list[int] = []
    nxt = 1
    for ch in cluster:
        out.append(f"-{ch}")
        origins.append(0)
        if f"-{ch}" not in cs.value_spellings:
            continue
        if nxt >= len(argv):
            return OldStyleArgv(out, origins, cluster, ch)
        out.append(argv[nxt])
        origins.append(nxt)
        nxt += 1
    for idx in range(nxt, len(argv)):
        out.append(argv[idx])
        origins.append(idx)
    return OldStyleArgv(out, origins, cluster)


__all__ = ["OldStyleArgv", "expand_old_style"]
