import click

from .decoders.data.decode import decode_qr_data, matrix_to_image, reconstruct_matrix
from .decoders.formatinfo import decode_format_version_information
from .decoders.mask import apply_mask
from .decoders.version import decode_version_information
from .matrix import load_matrix_from_file


@click.command()
@click.option(
    "-i",
    "--input-file",
    type=click.Path(exists=True),
    required=True,
    help="Path to the input data file.",
)
@click.option(
    "--ascii",
    "assume_ascii",
    is_flag=True,
    default=False,
    help="Assume payload is printable ASCII text (byte mode, MSB=0 per byte).",
)
@click.option(
    "--url",
    "assume_url",
    is_flag=True,
    default=False,
    help="Assume data starts with http:// or https:// (implies --ascii).",
)
@click.option(
    "--max-brute-bits",
    type=int,
    default=10,
    show_default=True,
    help="Max unknown bits to brute-force per RS block (2^N combinations). Higher values take exponentially longer.",
)
@click.option(
    "--parallel",
    is_flag=True,
    default=False,
    help="Distribute brute-force work across all CPU cores (useful with large --max-brute-bits).",
)
@click.option(
    "--reconstruct",
    type=click.Path(),
    default=None,
    help="Save the fully reconstructed QR code as an image to the given path.",
)
@click.option(
    "--transpose-reconstruct",
    is_flag=True,
    default=False,
    help="Transpose the reconstructed QR code before saving (to fix reconstruction when input is column-major).",
)
def main(
    input_file: str,
    assume_ascii: bool,
    assume_url: bool,
    max_brute_bits: int,
    parallel: bool,
    reconstruct: str | None,
    transpose_reconstruct: bool,
) -> None:
    matrix = load_matrix_from_file(input_file)
    format_info = decode_format_version_information(matrix)
    version_info = decode_version_information(matrix)

    print("Decoded format information:")
    print(f"  Error correction level: {format_info.error_correction_level}")
    print(f"  Mask pattern: {format_info.mask_pattern}")
    print(f"Decoded version information: {version_info.version if version_info else 'Unknown'}")

    if format_info.mask_pattern is None:
        print("Cannot decode format information, stopping here.")
        return

    flipped = apply_mask(matrix, format_info.mask_pattern, version_info.version)

    if format_info.error_correction_level is None:
        print("Cannot decode error correction level, so not applying error correction decoding.")
        print("Partially decoded (masked) matrix:")
        print(flipped)
        return

    result = decode_qr_data(
        flipped,
        version_info.version,
        format_info.error_correction_level,
        assume_ascii=assume_ascii,
        assume_url=assume_url,
        max_brute_bits=max_brute_bits,
        parallel=parallel,
    )

    print("\nData decoding:")
    print(f"  Data codewords: {result.known_data_codewords}/{result.total_data_codewords} known")
    print(f"  EC codewords: {result.known_ec_codewords}/{result.total_ec_codewords} known")
    print(f"  RS blocks corrected: {result.blocks_corrected}/{result.block_info.total_blocks}")
    print(f"  EC capacity per block: {result.block_info.ec_codewords_per_block} erasures")

    if result.constraint_stats:
        cs = result.constraint_stats
        parts = []
        if cs.get("prefix"):
            parts.append(f"URL prefix \"{cs['prefix']}\"")
        if cs.get("bits_set"):
            parts.append(f"{cs['bits_set']} bits set")
        if cs.get("codewords_resolved"):
            parts.append(f"{cs['codewords_resolved']} codewords resolved")
        if cs.get("padding_bits_set"):
            parts.append(f"{cs['padding_bits_set']} padding bits set")
        if cs.get("padding_codewords_resolved"):
            parts.append(f"{cs['padding_codewords_resolved']} padding codewords resolved")
        if parts:
            print(f"  Constraints: {', '.join(parts)}")

    if result.decoded.segments:
        print("\nDecoded segments:")
        for seg in result.decoded.segments:
            print(f"  [{seg.mode_name}] count={seg.char_count}: {repr(seg.data)}")
        print(f"\nRecovered text: {result.decoded.raw_text}")

    if reconstruct:
        recovered = reconstruct_matrix(matrix, result, format_info.mask_pattern)
        if transpose_reconstruct:
            recovered = recovered.transpose()
        matrix_to_image(recovered, reconstruct)
        print(f"\nReconstructed QR code saved to: {reconstruct}")

    if not result.decoded.segments:
        total_erasures_per_block = (
            result.total_data_codewords
            + result.total_ec_codewords
            - result.known_data_codewords
            - result.known_ec_codewords
        ) // result.block_info.total_blocks
        print("\nNo data segments could be decoded.")
        print(f"  Average erasures per block: ~{total_erasures_per_block}")
        print(
            f"  RS can correct up to {result.block_info.ec_codewords_per_block} erasures per block."
        )
        if total_erasures_per_block > result.block_info.ec_codewords_per_block:
            print(f"  (Too many unknowns for RS correction — need more of the QR code)")


if __name__ == "__main__":
    main()
