"""Generate SHEP Results Report PDF for Cuong's Contracts essay (May 22, 2026).

This report captures all feedback from the SHEP grading engine:
overall feedback, structural notes, issue-level feedback, and grader criteria.
"""

from reportlab.lib.pagesizes import letter
from reportlab.lib.units import inch
from reportlab.lib.colors import HexColor
from reportlab.lib.styles import ParagraphStyle
from reportlab.lib.enums import TA_LEFT, TA_JUSTIFY, TA_CENTER
from reportlab.platypus import (
    SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, HRFlowable,
)
import os

NAVY       = HexColor("#1a2744")
GOLD       = HexColor("#c9a227")
MED_TEXT   = HexColor("#2d3748")
LIGHT_TEXT = HexColor("#4a5568")
BORDER     = HexColor("#d4cfc5")
WHITE      = HexColor("#ffffff")
ACTION_BG  = HexColor("#f0f4ff")
STRENGTH_BG = HexColor("#f0faf0")
WARN_BG    = HexColor("#fef3e6")
RED_ACCENT = HexColor("#c53030")
AMBER_ACCENT = HexColor("#b7791f")
GREEN_ACCENT = HexColor("#276749")
LIGHT_GRAY = HexColor("#f7f7f5")

def make_styles():
    s = {}
    s["title"] = ParagraphStyle("title", fontName="Helvetica-Bold", fontSize=16,
        textColor=NAVY, alignment=TA_LEFT, spaceAfter=1, leading=19)
    s["subtitle"] = ParagraphStyle("subtitle", fontName="Helvetica", fontSize=9,
        textColor=LIGHT_TEXT, alignment=TA_LEFT, spaceAfter=4, leading=11)
    s["band_big"] = ParagraphStyle("band_big", fontName="Helvetica-Bold", fontSize=28,
        textColor=RED_ACCENT, alignment=TA_CENTER, leading=32)
    s["band_label"] = ParagraphStyle("band_label", fontName="Helvetica", fontSize=8,
        textColor=LIGHT_TEXT, alignment=TA_CENTER, leading=10)
    s["section_label"] = ParagraphStyle("section_label", fontName="Helvetica-Bold",
        fontSize=8.5, textColor=GOLD, spaceBefore=10, spaceAfter=3, leading=11)
    s["h1"] = ParagraphStyle("h1", fontName="Helvetica-Bold", fontSize=11,
        textColor=NAVY, spaceBefore=8, spaceAfter=3, leading=13)
    s["h2"] = ParagraphStyle("h2", fontName="Helvetica-Bold", fontSize=9,
        textColor=MED_TEXT, spaceBefore=5, spaceAfter=2, leading=11)
    s["badge_nw"] = ParagraphStyle("badge_nw", fontName="Helvetica-Bold",
        fontSize=7.5, textColor=RED_ACCENT, spaceBefore=0, spaceAfter=1, leading=10)
    s["badge_missed"] = ParagraphStyle("badge_missed", fontName="Helvetica-Bold",
        fontSize=7.5, textColor=AMBER_ACCENT, spaceBefore=0, spaceAfter=1, leading=10)
    s["body"] = ParagraphStyle("body", fontName="Helvetica", fontSize=8.5,
        textColor=MED_TEXT, alignment=TA_JUSTIFY, leading=11.5,
        spaceBefore=1, spaceAfter=3)
    s["quote"] = ParagraphStyle("quote", fontName="Helvetica-Oblique", fontSize=8,
        textColor=HexColor("#4a5568"), alignment=TA_LEFT, leading=10.5,
        leftIndent=12, spaceBefore=1, spaceAfter=1)
    s["grader_item"] = ParagraphStyle("grader_item", fontName="Helvetica", fontSize=8,
        textColor=HexColor("#4a5568"), alignment=TA_LEFT, leading=10.5,
        leftIndent=12, spaceBefore=1, spaceAfter=1)
    s["strength"] = ParagraphStyle("strength", fontName="Helvetica", fontSize=8.5,
        textColor=GREEN_ACCENT, alignment=TA_LEFT, leading=11.5,
        spaceBefore=1, spaceAfter=1)
    s["footer"] = ParagraphStyle("footer", fontName="Helvetica", fontSize=7,
        textColor=LIGHT_TEXT, alignment=TA_LEFT, leading=9)
    return s

ST = make_styles()

def thin_hr():
    return HRFlowable(width="100%", thickness=0.4, color=BORDER, spaceAfter=3, spaceBefore=4)

def gold_hr():
    return HRFlowable(width="25%", thickness=1, color=GOLD, spaceAfter=4, spaceBefore=2)

def shaded_box(text, style, bg, border_color=None):
    bc = border_color or BORDER
    t = Table([[Paragraph(text, style)]], colWidths=["100%"])
    t.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (-1,-1), bg),
        ("LEFTPADDING", (0,0), (-1,-1), 8), ("RIGHTPADDING", (0,0), (-1,-1), 8),
        ("TOPPADDING", (0,0), (-1,-1), 6), ("BOTTOMPADDING", (0,0), (-1,-1), 6),
        ("BOX", (0,0), (-1,-1), 0.3, bc),
    ]))
    return t

def grader_bullets(items):
    elements = []
    for item in items:
        elements.append(Paragraph("&#x2022;  " + item, ST["grader_item"]))
    return elements


def build():
    output_dir = r"C:\Users\sallen\Desktop\SHEP\BAR PREP by SHEP\STUDENT ANSWERS\Cuong"
    output_path = os.path.join(output_dir, "Cuong_Contracts_Results.pdf")
    doc = SimpleDocTemplate(output_path, pagesize=letter,
        leftMargin=0.65*inch, rightMargin=0.65*inch,
        topMargin=0.55*inch, bottomMargin=0.5*inch)
    story = []

    # ================================================================
    # HEADER
    # ================================================================
    story.append(Paragraph("Essay Evaluation Report", ST["title"]))
    story.append(gold_hr())
    story.append(Paragraph("CONTRACTS  |  Uploaded Practice  |  May 22, 2026  |  949 words", ST["subtitle"]))

    # Band score box
    band_data = [
        [Paragraph("2 / 6", ST["band_big"])],
        [Paragraph("SHEP PRACTICE BAND", ST["band_label"])],
        [Paragraph("Below Expectations", ST["band_label"])],
    ]
    band_table = Table(band_data, colWidths=[2*inch])
    band_table.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (-1,-1), LIGHT_GRAY),
        ("ALIGN", (0,0), (-1,-1), "CENTER"),
        ("TOPPADDING", (0,0), (0,0), 8),
        ("BOTTOMPADDING", (0,-1), (0,-1), 8),
        ("BOX", (0,0), (-1,-1), 0.5, BORDER),
    ]))
    story.append(band_table)
    story.append(Spacer(1, 6))

    # ================================================================
    # OVERALL FEEDBACK
    # ================================================================
    story.append(Paragraph("OVERALL FEEDBACK", ST["section_label"]))
    story.append(Paragraph(
        "This essay has critical problems that would likely result in a failing score. Your "
        "performance is characterized by a failure to apply legal rules to the facts provided. "
        "You frequently state a conclusion without providing the <b>analytical bridge</b> "
        "required to reach it. In legal writing, an analytical bridge requires you to state the "
        "rule, identify the relevant facts, and explain how those facts satisfy or fail the "
        "elements of the rule.",
        ST["body"]))
    story.append(Paragraph(
        "For example, when you conclude that the neighbor cannot sue the painter, you simply "
        "state there is no assignment. A stronger version would look like this: <i>'To establish "
        "a breach of contract, the plaintiff must show privity of contract. Here, the neighbor "
        "was not a party to the original agreement between the homeowner and the painter. Because "
        "there was no valid assignment of the contract to the neighbor, the neighbor lacks the "
        "necessary privity to maintain a cause of action against the painter.'</i>",
        ST["body"]))
    story.append(Paragraph(
        "Furthermore, your grasp of fundamental contract doctrines is inconsistent. You conflate "
        "assignment and delegation, misapply the doctrine of substantial performance, and "
        "completely overlook the third-party beneficiary issue. These are not minor oversights; "
        "they are foundational errors that prevent you from correctly resolving the legal "
        "questions presented. To improve, you must move away from conclusory statements and "
        "focus on the <i>why</i> behind your legal conclusions. You need to rigorously practice "
        "identifying the elements of a rule and explicitly linking them to the facts in the prompt.",
        ST["body"]))

    # ================================================================
    # FROM SHEP'S EYES (Structural Feedback)
    # ================================================================
    story.append(Paragraph("FROM SHEP'S EYES", ST["section_label"]))
    story.append(shaded_box(
        "You have a great handle on the mechanical side of essay writing. Your use of clear "
        "headings makes my job as a grader much easier because I can instantly see where you "
        "are in your analysis. You are doing the hard work of separating your thoughts, which "
        "is exactly what I want to see. Your conclusions, however, are a bit thin. They "
        "currently just repeat what you already said in the analysis section. A strong conclusion "
        "should tie the rule and the facts together one last time to show why the result is "
        "inevitable. For example, instead of saying 'A court would find that there is no valid "
        "contract,' try saying <i>'Because the painter never consented to the assignment of "
        "duties to the neighbor, there is no privity of contract, and the neighbor's claim for "
        "breach must fail.'</i> This shows me you are not just checking a box but are actually "
        "closing the loop on your argument.",
        ST["strength"], STRENGTH_BG, HexColor("#9ae6b4")))
    story.append(Spacer(1, 4))

    # ================================================================
    # ISSUE 1: Assignment of Rights
    # ================================================================
    story.append(thin_hr())
    story.append(Paragraph("NEEDS WORK", ST["badge_nw"]))
    story.append(Paragraph("Assignment of Rights from Homeowner to Neighbor", ST["h1"]))
    story.append(Paragraph(
        "This analysis contains a critical doctrinal error. You conflate the assignment of "
        "rights with the delegation of duties. An assignment involves the transfer of a "
        "<b>right to receive performance</b>, whereas a delegation involves the transfer of a "
        "<b>duty to perform</b>. By stating, \"A proper assignment allows an original party to "
        "assign the contractual obligations,\" you demonstrate a fundamental misunderstanding "
        "of contract law. Furthermore, your analysis is entirely conclusory. You state, "
        "\"Because there is no valid assignment... there is no valid contract,\" without "
        "explaining the legal requirements for a valid assignment or applying the facts of the "
        "prompt to those requirements. A grader needs to see you identify the specific elements "
        "of an assignment and then explain why the facts (or lack thereof) satisfy or fail "
        "those elements.",
        ST["body"]))

    story.append(Paragraph("What the grader looked for:", ST["h2"]))
    story.extend(grader_bullets([
        "The student fails to identify any specific facts from the prompt regarding the "
        "interaction between the homeowner and the neighbor, such as the existence of a "
        "written agreement or communication between the parties.",
        "The student concludes that there is no valid assignment but fails to connect any "
        "specific facts to the rule elements provided. The analysis is conclusory.",
    ]))

    # ================================================================
    # ISSUE 2: Painter's Breach Against Neighbor
    # ================================================================
    story.append(thin_hr())
    story.append(Paragraph("NEEDS WORK", ST["badge_nw"]))
    story.append(Paragraph("Painter's Breach of Contract Against Neighbor", ST["h1"]))
    story.append(Paragraph(
        "This section fails to provide the necessary legal framework to support your conclusion. "
        "You identify the issue of a breach of contract action, but you omit the essential rules "
        "regarding <b>privity of contract</b>. A breach of contract claim requires the existence "
        "of a valid contract between the parties. By simply stating, \"The neighbor will not "
        "succeed... because there is not a valid assignment,\" you rely on a circular argument. "
        "You must explain the legal standard for privity and then demonstrate why the facts show "
        "that no such relationship was created between the neighbor and the painter.",
        ST["body"]))

    story.append(Paragraph("What the grader looked for:", ST["h2"]))
    story.extend(grader_bullets([
        "The student fails to state the legal rules required to establish a breach of contract "
        "claim, such as the existence of a valid contract or privity.",
        "The analysis is conclusory. It states that the claim fails because there is no valid "
        "assignment, but it does not explain the legal requirement for an assignment or why the "
        "specific facts of the case fail to meet that requirement.",
    ]))

    # ================================================================
    # ISSUE 3: Neighbor's Liability to Painter
    # ================================================================
    story.append(thin_hr())
    story.append(Paragraph("NEEDS WORK", ST["badge_nw"]))
    story.append(Paragraph("Neighbor's Liability to Painter for Non-Payment", ST["h1"]))
    story.append(Paragraph(
        "Your analysis of implied-in-fact contracts is legally incorrect and lacks depth. You "
        "state, \"When the painter substantially performed... then the painter is a valid party "
        "to the valid contract with the neighbor.\" This is a misstatement of the law; "
        "<b>substantial performance</b> is a doctrine used to determine if a party has fulfilled "
        "their obligations under an <i>existing</i> contract, not a mechanism to create a "
        "contract where none existed. You must explain the requirements for an implied-in-fact "
        "contract -- specifically, the manifestation of mutual assent through conduct -- and "
        "apply the facts to those specific elements.",
        ST["body"]))

    story.append(Paragraph("What the grader looked for:", ST["h2"]))
    story.extend(grader_bullets([
        "The response mentions the possibility of a claim for payment against the neighbor, "
        "but it does not clearly identify or name the issue of the neighbor's liability to "
        "the painter as a distinct legal question to be resolved.",
        "The response mentions substantial performance but fails to articulate the legal "
        "standard for an implied-in-fact contract or the specific requirements for a neighbor's "
        "liability to a third-party painter in the absence of a direct contract.",
        "The response refers to the painter painting the house, but it does not connect "
        "specific facts from the prompt to the legal theory of neighbor liability.",
        "There is no substantive analysis connecting the facts to the rule elements. The "
        "response makes conclusory statements about the existence of a contract without "
        "explaining why the facts satisfy the requirements for an implied-in-fact contract.",
    ]))

    # ================================================================
    # ISSUE 4: Homeowner's Continuing Liability
    # ================================================================
    story.append(thin_hr())
    story.append(Paragraph("NEEDS WORK", ST["badge_nw"]))
    story.append(Paragraph("Homeowner's Continuing Liability to Painter", ST["h1"]))
    story.append(Paragraph(
        "While you correctly identify the issue, your analysis is purely conclusory. You state, "
        "\"The painter would succeed... because the homeowner transferred the contract "
        "obligations to the neighbor without the painter's consent.\" This is insufficient. "
        "You must explain the rule regarding the <b>delegator's continuing liability</b>: a "
        "delegation of duties does not automatically release the delegator from liability unless "
        "there is a <b>novation</b>. You failed to mention novation or the legal effect of the "
        "delegator's ongoing duty, which is the core of this issue.",
        ST["body"]))

    story.append(Paragraph("What the grader looked for:", ST["h2"]))
    story.extend(grader_bullets([
        "The student identifies the issue of the homeowner's liability but does not state the "
        "rule regarding the delegator's continuing liability after delegation.",
        "The student does not mention novation or explain the conditions under which a "
        "delegator would be released from liability.",
        "The analysis is conclusory. The student states the homeowner is liable without "
        "explaining why delegation does not release the original obligor.",
    ]))

    # ================================================================
    # MISSED ISSUE: Third-Party Beneficiary
    # ================================================================
    story.append(thin_hr())
    story.append(Paragraph("MISSED ISSUE", ST["badge_missed"]))
    story.append(Paragraph("Retiree's Third-Party Beneficiary Status", ST["h1"]))
    story.append(Paragraph(
        "You failed to address the issue of <b>third-party beneficiary status</b> entirely. "
        "This is a critical omission. In contract law, when a contract is made for the benefit "
        "of a third party, that party may have standing to sue for breach. By ignoring this "
        "doctrine, you missed a major component of the prompt. You must learn to identify when "
        "a contract involves a third party and apply the rules regarding <b>intended versus "
        "incidental beneficiaries</b>.",
        ST["body"]))

    story.append(Paragraph("What the grader looked for:", ST["h2"]))
    story.extend(grader_bullets([
        "The student does not identify or name the issue of third-party beneficiary status. "
        "The student frames the issue solely as a general breach of contract action.",
        "The student provides a general rule for contract formation but fails to state the "
        "legal standard for third-party beneficiary status.",
        "The student does not identify or apply facts relevant to third-party beneficiary status.",
        "The student does not analyze the issue of third-party beneficiary status, focusing "
        "instead on assignment and delegation.",
        "The student concludes that the retiree cannot sue for breach of contract because they "
        "are not a party to the contract, but does not address the third-party beneficiary theory.",
    ]))

    # ================================================================
    # FOOTER
    # ================================================================
    story.append(Spacer(1, 12))
    story.append(HRFlowable(width="100%", thickness=0.4, color=BORDER, spaceAfter=4, spaceBefore=4))
    story.append(Paragraph("Bar Prep by SHEP  |  Essay Evaluation Report  |  May 22, 2026", ST["footer"]))
    doc.build(story)
    return output_path


if __name__ == "__main__":
    path = build()
    print(f"Results report: {path}")
