#!/usr/bin/env python3

import argparse

try:
    from sweagent import TOOLS_DIR
except ImportError:
    pass
else:
    import sys

    default_lib = TOOLS_DIR / "windowed" / "lib"
    assert default_lib.is_dir()
    sys.path.append(str(default_lib))
    sys.path.append(str(TOOLS_DIR / "registry" / "lib"))

from windowed_file import FileNotOpened, TextNotFound, WindowedFile  # type: ignore
from flake8_utils import flake8, format_flake8_output  # type: ignore

RETRY_WITH_OUTPUT_TOKEN = "###SWE-AGENT-RETRY-WITH-OUTPUT###"

_NOT_FOUND = """Your edit was not applied (file not modified): Text {search!r} not found in displayed lines (or anywhere in the file).
Please modify your search string. Did you forget to properly handle whitespace/indentation?
You can also call `open` again to re-display the file with the correct context.
"""

_NOT_FOUND_IN_WINDOW_MSG = """Your edit was not applied (file not modified): Text {search!r} not found in displayed lines.

However, we found the following occurrences of your search string in the file:

{occurrences}

You can use the `goto` command to navigate to these locations before running the edit command again.
"""

_MULTIPLE_OCCURRENCES_MSG = """Your edit was not applied (file not modified): Found more than one occurrence of {search!r} in the currently displayed lines.
Please make your search string more specific (for example, by including more lines of context).
"""

_NO_CHANGES_MADE_MSG = """Your search and replace strings are the same. No changes were made. Please modify your search or replace strings."""

_SINGLE_EDIT_SUCCESS_MSG = """Text replaced. Please review the changes and make sure they are correct:

1. The edited file is correctly indented
2. The edited file does not contain duplicate lines
3. The edit does not break existing functionality

Edit the file again if necessary."""

_MULTIPLE_EDITS_SUCCESS_MSG = """Replaced {n_replacements} occurrences. Please review the changes and make sure they are correct:

1. The edited file is correctly indented
2. The edited file does not contain duplicate lines
3. The edit does not break existing functionality

Edit the file again if necessary."""

_LINT_ERROR_TEMPLATE = """Your proposed edit has introduced new syntax error(s). Please read this error message carefully and then retry editing the file.

ERRORS:

{errors}

This is how your edit would have looked if applied
------------------------------------------------
{window_applied}
------------------------------------------------

This is the original code before your edit
------------------------------------------------
{window_original}
------------------------------------------------

Your changes have NOT been applied. Please fix your edit command and try again.
DO NOT re-run the same failed edit command. Running it again will lead to the same error.
"""


def get_parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser()
    parser.add_argument("search", type=str)
    parser.add_argument("replace", type=str)
    parser.add_argument("replace_all", type=bool, nargs="?", default=False)
    return parser


def main(search: str, replace: str, replace_all: bool):
    try:
        wf = WindowedFile(exit_on_exception=False)
    except FileNotOpened:
        print("No file opened. Either `open` or `create` a file first.")
        exit(1)

    # Turn \\n into \n etc., i.e., undo the escaping
    # args.replace = args.replace.encode("utf8").decode("unicode_escape")

    if search == replace:
        print(_NO_CHANGES_MADE_MSG)
        print(RETRY_WITH_OUTPUT_TOKEN)
        exit(2)

    pre_edit_lint = flake8(wf.path)

    try:
        if not replace_all:
            window_text = wf.get_window_text()
            if window_text.count(search) > 1:
                print(_MULTIPLE_OCCURRENCES_MSG.format(search=search))
                print(RETRY_WITH_OUTPUT_TOKEN)
                exit(4)
            replacement_info = wf.replace_in_window(search, replace)
            # todo: Should warn if more than one occurrence was found?
        else:
            # todo: Give overview of all replaced occurrences/number of replacements
            replacement_info = wf.replace(search, replace)
    except TextNotFound:
        line_no_founds = wf.find_all_occurrences(search, zero_based=False)
        if line_no_founds:
            print(
                _NOT_FOUND_IN_WINDOW_MSG.format(
                    search=search, occurrences="\n".join([f"- line {line_no}" for line_no in line_no_founds])
                )
            )
        else:
            print(_NOT_FOUND.format(search=search))
        print(RETRY_WITH_OUTPUT_TOKEN)
        exit(3)

    post_edit_lint = flake8(wf.path)

    if not replace_all:
        # Try to filter out pre-existing errors
        replacement_window = (
            replacement_info.first_replaced_line,
            replacement_info.first_replaced_line + replacement_info.n_search_lines - 1,
        )
        # print(f"{replacement_info=}")
        # print(f"{replacement_window=}")
        # print(f"{pre_edit_lint=}")
        # print(f"{post_edit_lint=}")
        new_flake8_output = format_flake8_output(
            post_edit_lint,
            previous_errors_string=pre_edit_lint,
            replacement_window=replacement_window,
            replacement_n_lines=replacement_info.n_replace_lines,
        )
    else:
        # Cannot easily compare the error strings, because line number changes are hard to keep track of
        # So we show all linter errors.
        new_flake8_output = format_flake8_output(post_edit_lint)

    if new_flake8_output:
        with_edits = wf.get_window_text(line_numbers=True, status_line=True, pre_post_line=True)
        wf.undo_edit()
        without_edits = wf.get_window_text(line_numbers=True, status_line=True, pre_post_line=True)
        print(
            _LINT_ERROR_TEMPLATE.format(
                errors=new_flake8_output, window_applied=with_edits, window_original=without_edits,
            )
        )
        print(RETRY_WITH_OUTPUT_TOKEN)
        exit(4)
    if not replace_all:
        print(_SINGLE_EDIT_SUCCESS_MSG)
    else:
        print(_MULTIPLE_EDITS_SUCCESS_MSG.format(n_replacements=replacement_info.n_replacements))

    wf.print_window()


if __name__ == "__main__":
    main(**vars(get_parser().parse_args()))
