from models.model import Model


class Sequence(Model):
    def load(self):
        super().load()
        self._migrate_after_load()

    def _migrate_after_load(self):
        try:
            from models.profile import Profile

            profiles = Profile()
            profile_list = profiles.list()
            default_profile_id = profile_list[0] if profile_list else None
        except Exception:
            default_profile_id = None

        changed = False
        for _sid, doc in list(self.items()):
            if not isinstance(doc, dict):
                continue
            if not isinstance(doc.get("steps"), list):
                presets = doc.get("presets")
                if isinstance(presets, list) and presets:
                    doc["steps"] = [
                        {"preset_id": str(p), "group_ids": []} for p in presets
                    ]
                else:
                    doc["steps"] = []
                changed = True
            if "step_duration_ms" not in doc:
                dur = doc.get("sequence_duration")
                doc["step_duration_ms"] = (
                    int(dur) if isinstance(dur, (int, float)) else 3000
                )
                changed = True
            if "loop" not in doc:
                doc["loop"] = bool(doc.get("sequence_loop", False))
                changed = True
            if "name" not in doc:
                doc["name"] = str(doc.get("group_name") or "")
                changed = True
            if "profile_id" not in doc and default_profile_id is not None:
                doc["profile_id"] = str(default_profile_id)
                changed = True
            if not isinstance(doc.get("lanes"), list):
                steps = doc.get("steps")
                if isinstance(steps, list) and steps:
                    doc["lanes"] = [list(steps)]
                else:
                    doc["lanes"] = [[]]
                changed = True
            if "group_ids" not in doc or not isinstance(doc.get("group_ids"), list):
                doc["group_ids"] = []
                changed = True
            if doc.get("advance_mode") != "beats":
                doc["advance_mode"] = "beats"
                changed = True
            if "simulated_bpm" not in doc:
                doc["simulated_bpm"] = 120
                changed = True
            else:
                try:
                    sb = int(float(doc["simulated_bpm"]))
                    doc["simulated_bpm"] = max(30, min(300, sb))
                except (TypeError, ValueError):
                    doc["simulated_bpm"] = 120
                    changed = True
            if "sequence_transition" not in doc:
                doc["sequence_transition"] = 500
                changed = True
            # Ensure each step has beats (beat-based advance); default 1
            for lane in doc.get("lanes") or []:
                if not isinstance(lane, list):
                    continue
                for step in lane:
                    if not isinstance(step, dict):
                        continue
                    if "beats" not in step:
                        step["beats"] = 1
                        changed = True
            # Per-lane group ids (parallel to ``lanes``)
            lanes_list = [x for x in (doc.get("lanes") or []) if isinstance(x, list)]
            n_lanes = len(lanes_list)
            lg = doc.get("lanes_group_ids")
            if n_lanes and (not isinstance(lg, list) or len(lg) != n_lanes):
                shared = doc.get("group_ids") if isinstance(doc.get("group_ids"), list) else []
                shared_s = [str(x).strip() for x in shared if x is not None and str(x).strip()]
                if n_lanes == 1 and lanes_list[0]:
                    first = lanes_list[0][0] if isinstance(lanes_list[0][0], dict) else {}
                    step_g = (
                        first.get("group_ids")
                        if isinstance(first.get("group_ids"), list)
                        else []
                    )
                    step_s = [
                        str(x).strip() for x in step_g if x is not None and str(x).strip()
                    ]
                    doc["lanes_group_ids"] = [step_s if step_s else list(shared_s)]
                else:
                    doc["lanes_group_ids"] = [list(shared_s) for _ in range(n_lanes)]
                changed = True
        if changed:
            self.save()

    def create(self, profile_id=None):
        next_id = self.get_next_id()
        self[next_id] = {
            "name": "",
            "profile_id": str(profile_id) if profile_id is not None else None,
            "group_ids": [],
            "lanes": [[]],
            "lanes_group_ids": [[]],
            "advance_mode": "beats",
            "steps": [],
            "step_duration_ms": 3000,
            "simulated_bpm": 120,
            "sequence_transition": 500,
            "loop": True,
        }
        self.save()
        return next_id

    def read(self, id):
        id_str = str(id)
        return self.get(id_str, None)

    def update(self, id, data):
        id_str = str(id)
        if id_str not in self:
            return False
        if not isinstance(data, dict):
            return False
        data = dict(data)
        steps = data.get("steps")
        lanes = data.get("lanes")
        if isinstance(steps, list) and steps:
            lanes_ok = (
                isinstance(lanes, list)
                and lanes
                and any(isinstance(x, list) and len(x) > 0 for x in lanes)
            )
            if not lanes_ok:
                data["lanes"] = [list(steps)]
        self[id_str].update(data)
        self.save()
        return True

    def delete(self, id):
        id_str = str(id)
        if id_str not in self:
            return False
        self.pop(id_str)
        self.save()
        return True

    def list(self):
        return list(self.keys())
