"""Field specs for the admin panel's own add/edit forms.

Each spec is a plain list of sections; each section is a title and a list of
fields. A field is a dict — no Django forms involved, just enough description
for the template to render an input and for `apply_fields` to write the value
back onto the model instance.

Field types: text, email, url, textarea, date, number, money, select, checkbox,
file. `select` fields carry either `choices` (value/label pairs) or `queryset`
(resolved at request time by the view).
"""

from decimal import Decimal, InvalidOperation

from .models import (Client, Invoice, Lead, PortfolioCategory, PortfolioProject,
                     PricingPackage, Project, Quote, Service, Task, Ticket)


def f(name, label, kind="text", **extra):
    field = {"name": name, "label": label, "kind": kind}
    field.update(extra)
    return field


# ─────────────────────────── specs ───────────────────────────

LEAD_SPEC = [
    ("Who they are", [
        f("name", "Full name", required=True, placeholder="Ngozi Eze"),
        f("email", "Email", "email", required=True, placeholder="them@company.ng"),
        f("phone", "Phone", placeholder="+234 …"),
        f("company", "Company"),
    ]),
    ("What they want", [
        f("project_type", "Project type", placeholder="Business website"),
        f("description", "Description", "textarea"),
        f("features", "Features", placeholder="Comma separated"),
        f("budget", "Budget", placeholder="₦1m – ₦3m"),
        f("timeline", "Timeline", placeholder="1–3 months"),
        f("target_date", "Target date", "date"),
        f("references", "Reference websites", "textarea"),
        f("branding", "Logo and brand colours"),
    ]),
    ("Your side", [
        f("status", "Status", "select", choices=Lead.STATUS_CHOICES),
        f("notes", "Private notes", "textarea"),
    ]),
]

PROJECT_SPEC = [
    ("Overview", [
        f("client", "Client", "select", model=Client, required=True),
        f("name", "Project name", required=True, placeholder="ABC Company Website"),
        f("slug", "Slug", slugify_from="name",
          placeholder="leave blank to build it from the name"),
        f("project_type", "Type", placeholder="Business website"),
        f("description", "Description", "textarea"),
    ]),
    ("Money and dates", [
        f("budget", "Budget", "money", placeholder="850000"),
        f("start_date", "Start date", "date"),
        f("deadline", "Deadline", "date"),
        f("developer", "Assigned developer", placeholder="Teniola Ayodele"),
    ]),
    ("Progress", [
        f("status", "Status", "select", choices=Project.STATUS_CHOICES),
        f("progress", "Progress %", "number", min=0, max=100),
        f("internal_notes", "Internal notes", "textarea",
          help="Private to you. Clients never see these."),
    ]),
]

INVOICE_SPEC = [
    ("Invoice", [
        f("client", "Client", "select", model=Client, required=True),
        f("project", "Project", "select", model=Project, blank_label="None"),
        f("description", "Description", placeholder="50% deposit"),
        f("total", "Total", "money", required=True, placeholder="425000"),
    ]),
    ("Status and dates", [
        f("status", "Status", "select", choices=Invoice.STATUS_CHOICES),
        f("issued_on", "Issued on", "date"),
        f("due_on", "Due on", "date"),
    ]),
]

QUOTE_SPEC = [
    ("Quotation", [
        f("client", "Client", "select", model=Client, blank_label="None — quoting a lead"),
        f("lead", "Lead", "select", model=Lead, blank_label="None"),
        f("title", "What it is for", required=True, placeholder="Fleet tracking dashboard"),
        f("status", "Status", "select", choices=Quote.STATUS_CHOICES),
        f("valid_until", "Valid until", "date"),
        f("notes", "Notes", "textarea"),
    ]),
]

PORTFOLIO_SPEC = [
    ("Project", [
        f("name", "Project name", required=True),
        f("slug", "Slug", slugify_from="name",
          placeholder="leave blank to build it from the name"),
        f("client_name", "Client shown publicly", placeholder="ABC Company Ltd"),
        f("category", "Category", "select", model=PortfolioCategory, required=True),
        f("summary", "One-line summary", required=True,
          placeholder="Shown on the work cards"),
    ]),
    ("The case study", [
        f("description", "Description", "textarea"),
        f("problem", "The problem", "textarea"),
        f("features", "Features", "textarea", help="One per line."),
        f("services", "Services", placeholder="Comma separated"),
        f("technologies", "Technologies", placeholder="Comma separated"),
    ]),
    ("Links and publishing", [
        f("project_url", "Live URL", "url", placeholder="https://"),
        f("github_url", "GitHub URL", "url", placeholder="https://"),
        f("completed_on", "Completed on", "date"),
        f("featured", "Feature it on the homepage", "checkbox"),
        f("published", "Published — visible on the public site", "checkbox"),
    ]),
]

CATEGORY_SPEC = [
    ("Category", [
        f("name", "Name", required=True, placeholder="Web Apps"),
        f("slug", "Slug", slugify_from="name"),
    ]),
]

SERVICE_SPEC = [
    ("Service", [
        f("title", "Title", required=True, placeholder="Business websites"),
        f("slug", "Slug", slugify_from="title"),
        f("summary", "Summary", required=True),
        f("included", "What's included", "textarea", help="One item per line."),
    ]),
    ("Commercials", [
        f("starting_price", "Starting price", placeholder="From ₦350,000"),
        f("delivery_time", "Delivery time", placeholder="1–3 weeks"),
        f("order", "Sort order", "number"),
        f("published", "Show on the public site", "checkbox"),
    ]),
]

PACKAGE_SPEC = [
    ("Package", [
        f("name", "Name", required=True, placeholder="Business"),
        f("slug", "Slug", slugify_from="name"),
        f("audience", "Who it is for"),
        f("features", "Features", "textarea", help="One per line."),
    ]),
    ("Price", [
        f("price", "Price", required=True, placeholder="₦850,000"),
        f("price_note", "Price note", placeholder="one-off · from"),
        f("delivery_time", "Delivery", placeholder="Delivered in 3–4 weeks"),
        f("order", "Sort order", "number"),
        f("featured", "Highlight this package", "checkbox"),
        f("published", "Show on the public site", "checkbox"),
    ]),
]

CLIENT_SPEC = [
    ("Contact", [
        f("first_name", "First name", required=True),
        f("last_name", "Last name"),
        f("email", "Email", "email", required=True,
          help="This is the address they sign in with."),
        f("phone", "Phone", placeholder="+234 …"),
    ]),
    ("Business", [
        f("company", "Company"),
        f("address", "Address"),
        f("status", "Account status", "select", choices=Client.STATUS_CHOICES),
    ]),
]

TASK_SPEC = [
    ("Task", [
        f("name", "What needs doing", required=True,
          placeholder="Fix the careers form mailer"),
        f("project", "Project", "select", model=Project, blank_label="Not tied to a project"),
        f("priority", "Priority", "select", choices=Task.PRIORITY_CHOICES),
        f("status", "Column", "select", choices=Task.STATUS_CHOICES),
        f("due_date", "Due date", "date"),
        f("notes", "Notes", "textarea"),
    ]),
]

TICKET_SPEC = [
    ("Ticket", [
        f("client", "Client", "select", model=Client, required=True),
        f("project", "Project", "select", model=Project, blank_label="None"),
        f("subject", "Subject", required=True),
        f("message", "Message", "textarea", required=True),
        f("priority", "Priority", "select", choices=Ticket.PRIORITY_CHOICES),
        f("status", "Status", "select", choices=Ticket.STATUS_CHOICES),
    ]),
]


# ─────────────────────────── helpers ───────────────────────────

def flatten(spec):
    for _, fields in spec:
        for field in fields:
            yield field


def slugify(value):
    import re
    return re.sub(r"^-|-$", "", re.sub(r"[^a-z0-9]+", "-", str(value).lower().strip()))


def read_post(request, spec):
    """Pull every field in the spec out of request.POST."""
    values = {}
    for field in flatten(spec):
        name = field["name"]
        if field["kind"] == "checkbox":
            values[name] = name in request.POST
        else:
            values[name] = request.POST.get(name, "").strip()
    return values


def read_instance(instance, spec):
    """Fill the form from an existing record."""
    values = {}
    for field in flatten(spec):
        name = field["name"]
        value = getattr(instance, name, "")
        if field["kind"] == "checkbox":
            values[name] = bool(value)
        elif field["kind"] == "date":
            values[name] = value.isoformat() if value else ""
        elif field["kind"] == "select" and field.get("model"):
            values[name] = str(getattr(instance, name + "_id", "") or "")
        else:
            values[name] = "" if value is None else value
    return values


def validate(values, spec, errors=None):
    errors = errors if errors is not None else {}
    for field in flatten(spec):
        name = field["name"]
        if field.get("required") and not values.get(name):
            errors[name] = f"{field['label']} is needed."
        if field["kind"] == "email" and values.get(name) and "@" not in values[name]:
            errors[name] = "That does not look like an email address."
        if field["kind"] == "money" and values.get(name):
            try:
                Decimal(str(values[name]).replace(",", ""))
            except InvalidOperation:
                errors[name] = "Enter a number."
    return errors


def apply_fields(instance, values, spec, resolvers=None):
    """Write the cleaned values onto the model instance."""
    resolvers = resolvers or {}
    for field in flatten(spec):
        name, kind = field["name"], field["kind"]
        value = values.get(name)

        if kind == "checkbox":
            setattr(instance, name, bool(value))
        elif kind == "date":
            setattr(instance, name, value or None)
        elif kind == "money":
            setattr(instance, name, Decimal(str(value).replace(",", "")) if value else Decimal("0"))
        elif kind == "number":
            setattr(instance, name, int(value) if str(value).isdigit() else 0)
        elif kind == "select" and field.get("model"):
            setattr(instance, name, resolvers.get(name))
        else:
            if field.get("slugify_from") and not value:
                value = slugify(values.get(field["slugify_from"], ""))
                values[name] = value
            setattr(instance, name, value or "")
    return instance


def build_sections(spec, values, errors, querysets=None):
    """Shape the spec into what the template renders."""
    querysets = querysets or {}
    sections = []
    for title, fields in spec:
        rendered = []
        for field in fields:
            item = dict(field)
            item["value"] = values.get(field["name"], "")
            item["error"] = errors.get(field["name"], "")
            if field["kind"] == "select":
                if field.get("model"):
                    item["options"] = [
                        (str(o.pk), str(o)) for o in querysets.get(field["name"], [])
                    ]
                    item["blank_label"] = field.get("blank_label", "Choose one")
                else:
                    item["options"] = [(v, l) for v, l in field.get("choices", [])]
                    item["blank_label"] = field.get("blank_label")
            rendered.append(item)
        sections.append({"title": title, "fields": rendered})
    return sections
