import re
from typing import Optional, Type


class FormGrid:
    form_cls: Optional[Type] = None

    def __init__(self, data, initial, prefix, fabric=None):
        super(FormGrid, self).__init__()
        self.data = data
        self._forms = {}
        _exists_form_keys = []
        exists_pattern = re.compile(r'%s-(\d+)-\S+' % prefix)
        new_pattern = re.compile(r'%s-new(\d+)-\S+' % prefix)

        if fabric:
            form = fabric
        else:
            form = self.form_cls

        if self.data:
            for key in self.data:
                m = exists_pattern.match(key)
                if m:
                    i = int(m.groups()[0])
                    _exists_form_keys.append(i)

        for key, value in initial.items():
            if self.data and key not in _exists_form_keys:
                continue

            self._forms[key] = form(
                data=data,
                prefix='%s-%d' % (prefix, key),
                initial=value
            )

        if self.data:
            for key in self.data:
                m = new_pattern.match(key)
                if m:
                    i = int(m.groups()[0])
                    self._forms['new%d' % i] = form(
                        data=data,
                        prefix='%s-new%d' % (prefix, i),
                        initial={}
                    )

    def __len__(self):
        return len(self._forms)

    def __iter__(self):
        for k in sorted(self._forms.keys()):
            yield self._forms[k]

    def __contains__(self, key):
        return key in self._forms

    def is_valid(self):
        r = all(f.is_valid() for f in self._forms.values() if f.is_bound)
        if r:
            self.cleaned_data = {
                k: f.cleaned_data
                for k, f in self._forms.items() if f.is_bound
            }
        return r

    @property
    def errors(self):
        return dict((k, f.errors) for k, f in self._forms.items() if f.errors)
