"""Generate Contracts analysis PDF for Intesar (May 21, 2026)."""

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
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")
WARM_BG   = HexColor("#fdf6ee")
COOL_BG   = HexColor("#eef5ee")
ACTION_BG = HexColor("#f0f4ff")
REWRITE_BEFORE = HexColor("#fce8e8")
REWRITE_AFTER  = HexColor("#e2f0e2")

def make_styles():
    s = {}
    s["title"] = ParagraphStyle("title", fontName="Helvetica-Bold", fontSize=15,
        textColor=NAVY, alignment=TA_LEFT, spaceAfter=1, leading=18)
    s["subtitle"] = ParagraphStyle("subtitle", fontName="Helvetica", fontSize=9,
        textColor=LIGHT_TEXT, alignment=TA_LEFT, spaceAfter=8, leading=11)
    s["h1"] = ParagraphStyle("h1", fontName="Helvetica-Bold", fontSize=11,
        textColor=NAVY, spaceBefore=10, spaceAfter=3, leading=13)
    s["h2"] = ParagraphStyle("h2", fontName="Helvetica-Bold", fontSize=9,
        textColor=MED_TEXT, spaceBefore=5, spaceAfter=2, leading=11)
    s["body"] = ParagraphStyle("body", fontName="Helvetica", fontSize=8.5,
        textColor=MED_TEXT, alignment=TA_JUSTIFY, leading=11.5,
        spaceBefore=1, spaceAfter=3)
    s["essay"] = ParagraphStyle("essay", fontName="Helvetica", fontSize=8,
        textColor=HexColor("#374151"), alignment=TA_JUSTIFY, leading=10.5)
    s["action"] = ParagraphStyle("action", fontName="Helvetica", fontSize=8.5,
        textColor=HexColor("#1e3a5f"), alignment=TA_LEFT, leading=11.5,
        spaceBefore=1, spaceAfter=1)
    s["rewrite_label"] = ParagraphStyle("rewrite_label", fontName="Helvetica-Bold",
        fontSize=7.5, textColor=LIGHT_TEXT, leading=10, spaceBefore=0, spaceAfter=1)
    s["rewrite"] = ParagraphStyle("rewrite", fontName="Helvetica", fontSize=8,
        textColor=MED_TEXT, alignment=TA_JUSTIFY, leading=10.5)
    s["footer"] = ParagraphStyle("footer", fontName="Helvetica", fontSize=7,
        textColor=LIGHT_TEXT, alignment=TA_LEFT, leading=9)
    return s

ST = make_styles()

def shaded(text, bg, pad=6):
    t = Table([[Paragraph(text, ST["essay"])]], colWidths=["100%"])
    t.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (-1,-1), bg),
        ("LEFTPADDING", (0,0), (-1,-1), pad), ("RIGHTPADDING", (0,0), (-1,-1), pad),
        ("TOPPADDING", (0,0), (-1,-1), pad), ("BOTTOMPADDING", (0,0), (-1,-1), pad),
        ("BOX", (0,0), (-1,-1), 0.3, BORDER),
    ]))
    return t

def action_box(text):
    t = Table([[Paragraph(text, ST["action"])]], colWidths=["100%"])
    t.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (-1,-1), ACTION_BG),
        ("LEFTPADDING", (0,0), (-1,-1), 8), ("RIGHTPADDING", (0,0), (-1,-1), 8),
        ("TOPPADDING", (0,0), (-1,-1), 5), ("BOTTOMPADDING", (0,0), (-1,-1), 5),
    ]))
    return t

def rewrite_pair(before_text, after_text):
    before_parts = [
        [Paragraph("<b>CONCLUSORY</b>", ST["rewrite_label"]),
         Paragraph(before_text, ST["rewrite"])]
    ]
    after_parts = [
        [Paragraph("<b>REWRITTEN</b>", ST["rewrite_label"]),
         Paragraph(after_text, ST["rewrite"])]
    ]
    bt = Table(before_parts, colWidths=["100%"])
    bt.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (-1,-1), REWRITE_BEFORE),
        ("LEFTPADDING", (0,0), (-1,-1), 6), ("RIGHTPADDING", (0,0), (-1,-1), 6),
        ("TOPPADDING", (0,0), (-1,-1), 4), ("BOTTOMPADDING", (0,0), (-1,-1), 4),
        ("BOX", (0,0), (-1,-1), 0.3, BORDER),
    ]))
    at = Table(after_parts, colWidths=["100%"])
    at.setStyle(TableStyle([
        ("BACKGROUND", (0,0), (-1,-1), REWRITE_AFTER),
        ("LEFTPADDING", (0,0), (-1,-1), 6), ("RIGHTPADDING", (0,0), (-1,-1), 6),
        ("TOPPADDING", (0,0), (-1,-1), 4), ("BOTTOMPADDING", (0,0), (-1,-1), 4),
        ("BOX", (0,0), (-1,-1), 0.3, BORDER),
    ]))
    return [bt, Spacer(1, 2), at, Spacer(1, 6)]

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 build_contracts():
    output_dir = r"C:\Users\sallen\Desktop\SHEP\BAR PREP by SHEP\STUDENT ANSWERS\Intesar"
    output_path = os.path.join(output_dir, "Intesar_Contracts_Analysis.pdf")
    doc = SimpleDocTemplate(output_path, pagesize=letter,
        leftMargin=0.65*inch, rightMargin=0.65*inch,
        topMargin=0.55*inch, bottomMargin=0.5*inch)
    story = []

    story.append(Paragraph("Deepening Your Analysis: Contracts", ST["title"]))
    story.append(gold_hr())
    story.append(Paragraph("Intesar S.  |  Band 4/6  |  May 21, 2026", ST["subtitle"]))
    story.append(Paragraph(
        "On the bar exam, <b>analysis</b> is where you earn the most points. The rule statement "
        "shows you know the law. The analysis shows you can <i>use</i> it. Analysis means taking "
        "the specific facts from the prompt and explaining, step by step, why they satisfy or fail "
        "each element of the rule. A conclusion without this reasoning is just an assertion -- the "
        "grader has no evidence you understood the problem. Below, we compare the analysis portions "
        "of your essay against a model answer, with specific ways to strengthen each one.",
        ST["body"]))
    story.append(Spacer(1, 2))

    # ---- Issue 1: Minor's Contract ----
    story.append(thin_hr())
    story.append(Paragraph("Issue 1: Incapacity (Minor's Contract)", ST["h1"]))
    story.append(Paragraph("Your analysis:", ST["h2"]))
    story.append(shaded(
        "Finch turned 18 on May 1st. On May 15, while still 18, he made his regular payment "
        "affirming the contractual agreement with Vole's Vintage Vehicles because the payment "
        "after turning 18 continues the contract, therefore affirming the contract. Furthermore, "
        "on June 5th, Finch attempts to disaffirm the contract because it was entered into when "
        "he was a minor. However, due to the vehicle being a 'necessity' and Finch affirming the "
        "contract after a reasonable time of turning 18, a court would likely not permit Finch "
        "to disaffirm. Vole's refusal to accept the car or refund Finch's payment is proper "
        "because Finch affirmed the contract and the vehicle was used as a necessity.",
        WARM_BG))
    story.append(Paragraph("Model analysis:", ST["h2"]))
    story.append(shaded(
        "Finch was 17 when he signed the contract, making it voidable. The key question is "
        "whether his single payment on May 15, after turning 18, constituted ratification. While "
        "making a payment is conduct that can indicate ratification, it is not conclusive. Finch "
        "disaffirmed on June 5, less than five weeks after his 18th birthday -- likely a reasonable "
        "time. An argument could be made that the car was a necessary, as he needed it for his job "
        "which was his family's primary income source and no public transit was available. However, "
        "even if it were a necessary, he would only be liable for the reasonable value of its use "
        "and could still disaffirm the remaining obligations. Therefore, his disaffirmance was "
        "timely and the contract is not enforceable against him.",
        COOL_BG))
    story.append(Paragraph(
        "You reached the opposite conclusion from the model -- and the model's position is the "
        "stronger one. The critical error is treating a single post-majority payment as definitive "
        "ratification. One installment payment, made only two weeks after turning 18, is not enough "
        "to show an unequivocal intent to be bound. Courts look for more -- continued performance "
        "over a longer period, express statements, or conduct clearly inconsistent with "
        "disaffirmance. More importantly, you conflated the necessity doctrine with a bar to "
        "disaffirmance. Even when a contract involves a necessary, the minor can still disaffirm "
        "-- they are only liable for the <i>reasonable value</i> of what they received, not the "
        "full contract price. The model separates these two questions; your analysis merged them "
        "into a single conclusion.",
        ST["body"]))
    story.append(action_box(
        "<b>Build this habit:</b> When two doctrines point in different directions, analyze "
        "each one separately before reaching a conclusion. \"The payment could suggest "
        "ratification, but a single payment is not conclusive. Even if the car is a necessary, "
        "that limits liability -- it does not prevent disaffirmance.\" Separating the threads "
        "is where the points are."))

    # ---- Issue 2: Duress ----
    story.append(thin_hr())
    story.append(Paragraph("Issue 2: Duress (Heron's Cafe)", ST["h1"]))
    story.append(Paragraph("Your analysis:", ST["h2"]))
    story.append(shaded(
        "An improper threat is present when it forces a party to act by threatening a release "
        "of private information that is not public knowledge. Duress is enforcing a party to act "
        "unwillingly and left with no alternative means. Here, Badger threatened to expose "
        "Heron's private tax history. The cafe owner felt there was no alternative means and in "
        "order to avoid embarrassment, she must sign the agreement under unfair duress. "
        "Therefore, the contract is voidable by the cafe owner because she was offered an "
        "unreasonable amount while being threatened.",
        WARM_BG))
    story.append(Paragraph("Model analysis:", ST["h2"]))
    story.append(shaded(
        "Badger's threat was improper under Restatement &sect; 176 because he used power for "
        "illegitimate ends -- threatening to reveal embarrassing private information to coerce "
        "a party into a contract on unfair terms. The threat left Heron with no reasonable "
        "alternative: as a cafe owner reliant on her community standing, she reasonably feared "
        "public embarrassment and loss of customers. The integration clause is irrelevant because "
        "parol evidence is admissible to show a contract was entered into under duress, which "
        "goes to the formation of the contract itself. Therefore, the contract is voidable.",
        COOL_BG))
    story.append(Paragraph(
        "You spotted the right issue and reached the right conclusion. Two gaps keep this from "
        "earning full marks. First, you never addressed the integration clause. The prompt "
        "included it deliberately -- it is testing whether you know that duress goes to "
        "<i>formation</i>, not <i>interpretation</i>, so the clause is irrelevant. Ignoring it "
        "tells the grader you either missed the trap or did not know the distinction. Second, "
        "your explanation of 'no reasonable alternative' is circular: you said she had no "
        "alternative because she was under duress. The model explains <i>why</i> she had no "
        "alternative -- her livelihood depended on her community reputation, and the threat "
        "targeted exactly that vulnerability.",
        ST["body"]))
    story.append(action_box(
        "<b>Build this habit:</b> When a prompt includes a specific contract clause, address "
        "it explicitly -- even if only to explain why it does not apply. \"The integration clause "
        "does not bar Heron's defense because duress challenges the formation of the contract, "
        "not its terms.\" One sentence neutralizes the trap and earns the point."))

    # ---- Issue 3: Mutual Mistake ----
    story.append(thin_hr())
    story.append(Paragraph("Issue 3: Mutual Mistake (Sparrow's Land)", ST["h1"]))
    story.append(Paragraph("Your analysis:", ST["h2"]))
    story.append(shaded(
        "Mutual mistake is when both parties are mistaken as to the contract's contents. Here, "
        "the final written contract stated the parcel was being sold 'as is' for $500,000, a "
        "price both parties believed reflected fair market value for 10 acres. After the sale, "
        "Sparrow discovered the parcel was only 6 acres. It can be argued that the outdated "
        "county survey map is a mistake of Sparrow by not enforcing a new survey until after "
        "the contract was completed. Nevertheless, that argument would likely not succeed for "
        "the survey was public record and legally acquired. Therefore, the contract can be "
        "rescinded under mutual mistake because Sparrow did not assume the risk by accepting "
        "the property 'as is.'",
        WARM_BG))
    story.append(Paragraph("Model analysis:", ST["h2"]))
    story.append(shaded(
        "First, there was a mutual mistake -- both parties believed the parcel was 10 acres based "
        "on an outdated map. Second, the size of the parcel is a basic assumption of the contract. "
        "Third, the effect was material: a 40% reduction from 10 to 6 acres made Sparrow's "
        "planned project impossible. The final question is risk allocation. Crane will argue the "
        "'as is' clause allocated the risk to Sparrow. However, courts typically interpret 'as is' "
        "clauses as allocating risk of defects in quality or condition, not a mistake as to "
        "fundamental quantity. The phrase 'believed to be approximately 10 acres' further shows "
        "that the 10-acre size was a shared assumption, not a risk Sparrow accepted. Because all "
        "elements are met and the 'as is' clause likely did not allocate this risk, Sparrow can "
        "rescind.",
        COOL_BG))
    story.append(Paragraph(
        "You reached the right conclusion, and you showed good instinct by raising and dismissing "
        "the counterargument about Sparrow's failure to survey. Two things would strengthen this "
        "significantly. First, the model breaks mutual mistake into its elements -- (1) shared "
        "mistake, (2) basic assumption, (3) material effect, (4) risk allocation -- and applies "
        "each one separately. Your analysis jumps from 'both were wrong about the acreage' to "
        "'therefore rescission.' Walking through the elements shows the grader you know the "
        "full test. Second, your treatment of the 'as is' clause is conclusory. The model "
        "explains <i>why</i> it does not apply: 'as is' covers defects in quality or condition, "
        "not a mistake about the fundamental quantity of what was sold. That distinction is "
        "the key analytical move.",
        ST["body"]))
    story.append(action_box(
        "<b>Build this habit:</b> When a legal test has numbered elements, walk through each "
        "one in order. \"First, both parties shared the mistake. Second, acreage was a basic "
        "assumption. Third, the 40% shortfall is material. Fourth, the 'as is' clause covers "
        "condition, not quantity.\" Element-by-element application is the fastest way to "
        "demonstrate thorough analysis."))

    # ---- Conclusory vs. Proper ----
    story.append(Spacer(1, 4))
    story.append(HRFlowable(width="100%", thickness=0.8, color=GOLD, spaceAfter=4, spaceBefore=6))
    story.append(Paragraph("Conclusory Analysis vs. Proper Analysis", ST["h1"]))
    story.append(Paragraph(
        "Below are specific sentences from your essay rewritten to show the difference. The "
        "conclusory version states the outcome. The rewritten version explains <i>why</i> the "
        "facts lead there.",
        ST["body"]))

    # Rewrite 1
    story.append(Paragraph("<i>Issue 1 -- Ratification by payment</i>", ST["h2"]))
    story.extend(rewrite_pair(
        "On May 15, while still 18, Finch made his regular payment affirming the contractual "
        "agreement because the payment after turning 18 continues the contract, therefore "
        "affirming the contract.",
        "Finch made one payment two weeks after turning 18. While continued performance can "
        "indicate ratification, a single installment is not conclusive evidence of an intent "
        "to be bound. When he disaffirmed on June 5 -- barely five weeks after majority -- the "
        "disaffirmance was timely."
    ))

    # Rewrite 2
    story.append(Paragraph("<i>Issue 1 -- Necessity doctrine</i>", ST["h2"]))
    story.extend(rewrite_pair(
        "Due to the vehicle being a 'necessity' and Finch affirming the contract after a "
        "reasonable time of turning 18, a court would likely not permit Finch to disaffirm.",
        "Even if the car qualifies as a necessary because Finch needed it for his family's "
        "primary income and no public transit existed, the necessity doctrine does not prevent "
        "disaffirmance -- it only makes the minor liable for the reasonable value of what he "
        "received, not the full contract price."
    ))

    # Rewrite 3
    story.append(Paragraph("<i>Issue 2 -- No reasonable alternative</i>", ST["h2"]))
    story.extend(rewrite_pair(
        "The cafe owner felt there was no alternative means and in order to avoid embarrassment "
        "she must sign the agreement under unfair duress.",
        "Heron had no reasonable alternative because her livelihood depended on her community "
        "reputation. Badger's threat targeted exactly that vulnerability -- a story about tax "
        "problems could drive away the cafe customers she relied on. Signing was the only way "
        "to prevent that harm."
    ))

    # Rewrite 4
    story.append(Paragraph("<i>Issue 3 -- The 'as is' clause</i>", ST["h2"]))
    story.extend(rewrite_pair(
        "Sparrow did not assume the risk by accepting the property 'as is' and Sparrow's "
        "failure to survey before and depending on legal public documents is appropriate.",
        "The 'as is' clause typically allocates risk of defects in a property's quality or "
        "condition -- not a fundamental mistake about its quantity. A 40% shortfall in acreage "
        "is not a defect Sparrow assumed; it is a shared factual error that goes to the basic "
        "assumption of the deal."
    ))

    # ---- Closing ----
    story.append(HRFlowable(width="100%", thickness=0.4, color=BORDER, spaceAfter=4, spaceBefore=6))
    story.append(Paragraph(
        "This is your third essay, and the progression is visible. Your issue spotting is now "
        "complete across all three calls -- you identified every major doctrine. Your rule statements "
        "are accurate. The persistent gap remains the same: analysis depth. You state conclusions "
        "without the element-by-element reasoning that earns the highest marks. Three specific "
        "patterns to build: (1) when a legal test has numbered elements, walk through each one "
        "separately; (2) when two doctrines interact, analyze each independently before combining "
        "them into a conclusion; (3) when the prompt includes a specific clause or fact, address it "
        "explicitly -- even if only to explain why it does not change the outcome. These are the "
        "same habits from your Property and Business Associations reports. They are becoming more "
        "consistent. Keep pushing.",
        ST["body"]))
    story.append(Spacer(1, 10))
    story.append(Paragraph("Bar Prep by SHEP  |  May 21, 2026", ST["footer"]))
    doc.build(story)
    return output_path


if __name__ == "__main__":
    path = build_contracts()
    print(f"Contracts: {path}")
