import argparse
from hashlib import sha256
import json
from pathlib import Path

from pypdf.errors import PdfReadError
from pdf_pages import extract_pages


def has_quote(pdf_bytes, page, quote):
    pages = extract_pages(pdf_bytes)
    return 1 <= page <= len(pages) and quote in " ".join(pages[page - 1][1].split())


def record(pdf, page, quote, target):
    pdf_bytes = pdf.read_bytes()
    quote = " ".join(quote.split())
    if not quote or not has_quote(pdf_bytes, page, quote):
        raise ValueError("The quote is missing from the embedded text of that page.")
    proof = {
        "source": pdf.name,
        "sha256": sha256(pdf_bytes).hexdigest(),
        "page": page,
        "quote": quote,
    }
    with target.open("x", encoding="utf-8") as file:
        json.dump(proof, file, ensure_ascii=False, indent=2)
        file.write("\n")
    print(f"Recorded page {page} in {target}")


def verify(pdf, target):
    proof = json.loads(target.read_text(encoding="utf-8"))
    pdf_bytes = pdf.read_bytes()
    page = proof["page"]
    quote = proof["quote"]
    if type(page) is not int or not isinstance(quote, str) or not quote.strip():
        raise ValueError("The citation record needs a page number and quote.")
    if sha256(pdf_bytes).hexdigest() != proof["sha256"]:
        present = "still appears" if has_quote(pdf_bytes, page, quote) else "is missing"
        raise ValueError(f"PDF revision changed. The quote {present} on page {page}. Review the citation.")
    if not has_quote(pdf_bytes, page, quote):
        raise ValueError("The quote is missing from the recorded page.")
    print(f"Verified document bytes and page {page}: {quote}")


if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Pin a PDF citation to the source bytes and page.")
    commands = parser.add_subparsers(dest="command", required=True)
    new = commands.add_parser("record")
    new.add_argument("pdf", type=Path)
    new.add_argument("page", type=int)
    new.add_argument("quote")
    new.add_argument("target", type=Path)
    check = commands.add_parser("verify")
    check.add_argument("pdf", type=Path)
    check.add_argument("target", type=Path)
    args = parser.parse_args()
    try:
        if args.command == "record":
            record(args.pdf, args.page, args.quote, args.target)
        else:
            verify(args.pdf, args.target)
    except (OSError, KeyError, TypeError, ValueError, PdfReadError) as error:
        raise SystemExit(str(error)) from None
