import logging
import re
from collections import defaultdict

from pyramid.httpexceptions import HTTPFound
from pyramid.interfaces import IRoutesMapper

from ttfrog.db.manager import db


def get_all_routes(request):
    routes = {
        "static": "/static",
    }
    uri_pattern = re.compile(r"^([^\{\*]+)")
    mapper = request.registry.queryUtility(IRoutesMapper)
    for route in mapper.get_routes():
        if route.name.startswith("__"):
            continue
        m = uri_pattern.search(route.pattern)
        if m:
            routes[route.name] = m.group(0)
    return routes


class BaseController:
    model = None
    model_form = None

    def __init__(self, request):
        self.request = request
        self.attrs = defaultdict(str)
        self._slug = None
        self._record = None
        self._form = None

        self.config = {"static_url": "/static", "project_name": "TTFROG"}
        self.configure_for_model()

    @property
    def slug(self):
        if not self._slug:
            parts = self.request.matchdict.get("uri", "").split("-")
            self._slug = parts[0].replace("/", "")
        return self._slug

    @property
    def record(self):
        if not self._record and self.model:
            try:
                self._record = db.query(self.model).filter(self.model.slug == self.slug)[0]
            except IndexError:
                logging.warning(f"Could not load record with slug {self.slug}")
                self._record = self.model()
        return self._record

    @property
    def form(self):
        if not self.model:
            return
        if not self.model_form:
            return
        if not self._form:
            if self.request.POST:
                self._form = self.model_form(self.request.POST, obj=self.record)
            else:
                self._form = self.model_form(obj=self.record)
                if not self.record.id:
                    # apply the db schema defaults
                    self._form.process()
        return self._form

    @property
    def resources(self):
        return [
            {"type": "style", "uri": "css/styles.css"},
        ]

    def configure_for_model(self):
        if "all_records" not in self.attrs:
            self.attrs["all_records"] = db.query(self.model).all()

    def template_context(self, **kwargs) -> dict:
        return dict(
            config=self.config,
            request=self.request,
            form=self.form,
            record=self.record,
            routes=get_all_routes(self.request),
            resources=self.resources,
            **self.attrs,
            **kwargs,
        )

    def populate(self):
        self.form.populate_obj(self.record)

    def populate_association(self, key, formdata):
        populated = []
        for field in formdata:
            map_id = field.pop("id")
            map_id = int(map_id) if map_id else 0
            if not field[key]:
                continue
            elif not map_id:
                populated.append(field)
            else:
                field["id"] = map_id
                populated.append(field)
        return populated

    def validate(self):
        return self.form.validate()

    def save(self):
        if not self.form.save.data:
            return
        if not self.validate():
            return
        logging.debug(f"{self.form.data = }")
        # previous = dict(self.record)
        logging.debug(f"{self.record = }")
        self.populate()
        # transaction_log.record(previous, self.record)
        with db.transaction():
            db.add(self.record)
            self.save_callback()
            logging.debug(f"Saved {self.record = }")
            location = self.request.current_route_path()
            if self.record.slug not in location:
                location = f"{location}/{self.record.uri}"
            logging.debug(f"Redirecting to {location}")
            return HTTPFound(location=location)

    def delete(self):
        if not self.record.id:
            return
        with db.transaction():
            db.query(self.model).filter_by(id=self.record.id).delete()
            location = self.request.current_route_path()
            return HTTPFound(location=location)

    def response(self):
        if not self.form:
            return
        elif self.form.save.data:
            return self.save()
        elif self.form.delete.data:
            return self.delete()
