import os
import shlex
import pathlib


class SplitFileTarget:
    def __init__(self, slice_start_idx, test_path, lines, name):
        self.slice_start_idx = slice_start_idx
        self.test_path = test_path
        self.lines = lines
        self.name = name

    def copyFrom(self, source, leading_lines=False):
        lines_before = self.lines[: self.slice_start_idx + 1]
        self.lines = self.lines[self.slice_start_idx + 1 :]
        slice_end_idx = None
        for i, l in enumerate(self.lines):
            if SplitFileTarget._get_split_line_path(l) != None:
                slice_end_idx = i
                break
        if slice_end_idx is not None:
            lines_after = self.lines[slice_end_idx:]
        else:
            lines_after = []
        with open(source, "r") as f:
            source_lines = f.readlines()
        if leading_lines:
            # Strip the leading blank lines added by split-file --leading-lines
            source_lines = source_lines[self.slice_start_idx + 1 :]
        new_lines = lines_before + source_lines + lines_after
        with open(self.test_path, "w") as f:
            for l in new_lines:
                f.write(l)

    def __str__(self):
        return f"slice {self.name} in {self.test_path}"

    @staticmethod
    def get_target_dir(commands, test_path):
        # posix=True breaks Windows paths because \ is treated as an escaping character
        for cmd in commands:
            split = shlex.split(cmd, posix=False)
            if "split-file" not in split:
                continue
            start_idx = split.index("split-file")
            split = split[start_idx:]
            # Skip flags like --leading-lines
            leading_lines = "--leading-lines" in split
            args = [s for s in split[1:] if not s.startswith("-")]
            if len(args) < 2:
                continue
            p = unquote(args[0].strip())
            if not test_path.samefile(p):
                continue
            return (unquote(args[1].strip()), leading_lines)
        return None

    @staticmethod
    def create(path, commands, test_path, target_dir):
        path = pathlib.Path(path)
        with open(test_path, "r") as f:
            lines = f.readlines()
        for i, l in enumerate(lines):
            p = SplitFileTarget._get_split_line_path(l)
            if p and path.samefile(os.path.join(target_dir, p)):
                idx = i
                break
        else:
            return None
        return SplitFileTarget(idx, test_path, lines, p)

    @staticmethod
    def _get_split_line_path(l):
        if len(l) < 6:
            return None
        if l.startswith("//"):
            l = l[2:]
        else:
            l = l[1:]
        if l.startswith("--- "):
            l = l[4:]
        else:
            return None
        return l.rstrip()


def unquote(s):
    if len(s) > 1 and s[0] == s[-1] and (s[0] == '"' or s[0] == "'"):
        return s[1:-1]
    return s


def propagate_split_files(test_path, updated_files, commands):
    test_path = pathlib.Path(test_path)
    result = SplitFileTarget.get_target_dir(commands, test_path)
    if not result:
        return updated_files
    split_target_dir, leading_lines = result

    new = []
    for file in updated_files:
        target = SplitFileTarget.create(file, commands, test_path, split_target_dir)
        if target:
            target.copyFrom(file, leading_lines)
            new.append(str(target))
        else:
            new.append(file)
    return new
