from __future__ import annotations

import json
import re
import uuid
from typing import TYPE_CHECKING, Optional
from urllib.parse import parse_qs, urlparse

from mockmesh.interceptors.aws.s3 import read_body

if TYPE_CHECKING:
    from mockmesh.storage.manager import StorageManager

_ACCOUNT = "123456789012"


def _parse_sqs_params(req: dict) -> dict:
    result: dict = {}

    url = req.get("url", "") if isinstance(req, dict) else ""
    if url:
        qs = urlparse(url).query
        if qs:
            result.update({k: v[0] for k, v in parse_qs(qs).items()})

    try:
        raw  = read_body(req)
        if raw:
            text = raw.decode("utf-8", errors="replace").strip()
            if text.startswith("{"):
                try:
                    jd = json.loads(text)
                    for k, v in jd.items():
                        if isinstance(v, (list, dict)):
                            result[f"__json_{k}"] = v
                        else:
                            result[k] = str(v)
                except json.JSONDecodeError:
                    pass
            else:
                result.update({k: v[0] for k, v in parse_qs(text).items()})
    except Exception:
        pass

    return result


def _queue_name(req: dict, params: dict) -> str:
    url = req.get("url", "")
    if url:
        segment = url.rstrip("/").split("/")[-1]
        if segment and not segment.endswith(".com") and not segment.endswith(".net"):
            return segment

    body_url = (params.get("QueueUrl") or "").rstrip("/")
    if body_url:
        return body_url.split("/")[-1] or "default"

    try:
        raw = read_body(req)
        if raw:
            text = raw.decode("utf-8", errors="replace").strip()
            if text.startswith("{"):
                q_url = (json.loads(text).get("QueueUrl") or "").rstrip("/")
                if q_url:
                    return q_url.split("/")[-1] or "default"
    except Exception:
        pass

    return "default"


def _parse_batch_entries(params: dict, prefix: str) -> list:
    entries: dict = {}
    pattern = re.compile(rf"^{re.escape(prefix)}\.(?:member\.)?(\d+)\.(.+)$")
    for key, val in params.items():
        m = pattern.match(key)
        if m:
            n, field = int(m.group(1)), m.group(2)
            entries.setdefault(n, {})[field] = val
    return [entries[n] for n in sorted(entries)]


def handle_sqs(sm: "StorageManager", req: dict, operation: str) -> Optional[dict]:
    params = _parse_sqs_params(req)
    q      = _queue_name(req, params)

    if operation == "SendMessage":
        mid  = str(uuid.uuid4())
        body = params.get("MessageBody") or params.get("__json_MessageBody") or "{}"
        if isinstance(body, dict):
            body = json.dumps(body)
        sm.sqs_enqueue(q, mid, body)
        return {"MessageId": mid, "MD5OfMessageBody": "mockhash"}

    if operation == "SendMessageBatch":
        json_entries = params.get("__json_Entries")
        entries      = json_entries if isinstance(json_entries, list) \
                       else _parse_batch_entries(params, "SendMessageBatchRequestEntry")
        successful = []
        for entry in entries:
            mid = str(uuid.uuid4())
            sm.sqs_enqueue(q, mid, entry.get("MessageBody", "{}"))
            successful.append({"Id": entry.get("Id", mid), "MessageId": mid,
                                "MD5OfMessageBody": "mockhash"})
        return {"Successful": successful, "Failed": []}

    if operation == "ReceiveMessage":
        raw_max  = params.get("MaxNumberOfMessages", "1")
        max_msgs = int(raw_max) if str(raw_max).isdigit() else 1
        messages = []
        for _ in range(max_msgs):
            msg = sm.sqs_dequeue(q)
            if not msg:
                break
            messages.append({
                "MessageId":     msg["MessageId"],
                "ReceiptHandle": "mock-receipt-" + msg["MessageId"],
                "Body":          msg["Body"],
                "MD5OfBody":     "mockhash",
                "Attributes": {
                    "SentTimestamp":                    "1700000000000",
                    "ApproximateReceiveCount":          "1",
                    "ApproximateFirstReceiveTimestamp": "1700000000001",
                },
            })
        return {"Messages": messages}

    if operation == "DeleteMessage":
        handle = params.get("ReceiptHandle", "")
        sm.nosql.delete(f"sqs_{q}", handle.replace("mock-receipt-", ""))
        return {}

    if operation == "DeleteMessageBatch":
        json_entries = params.get("__json_Entries")
        entries      = json_entries if isinstance(json_entries, list) \
                       else _parse_batch_entries(params, "DeleteMessageBatchRequestEntry")
        successful = []
        for entry in entries:
            handle = entry.get("ReceiptHandle", "")
            sm.nosql.delete(f"sqs_{q}", handle.replace("mock-receipt-", ""))
            successful.append({"Id": entry.get("Id", "")})
        return {"Successful": successful, "Failed": []}

    if operation == "ChangeMessageVisibility":
        return {}

    if operation == "PurgeQueue":
        col = f"sqs_{q}"
        for record in sm.nosql.list(col):
            sm.nosql.delete(col, record["key"])
        return {}

    if operation == "GetQueueUrl":
        qname = params.get("QueueName") or q
        return {"QueueUrl": f"https://sqs.us-east-1.amazonaws.com/{_ACCOUNT}/{qname}"}

    if operation == "ListQueues":
        q_urls = [
            f"https://sqs.us-east-1.amazonaws.com/{_ACCOUNT}/{c[4:]}"
            for c in sm.nosql.list_collections()
            if c.startswith("sqs_")
        ]
        return {"QueueUrls": q_urls}

    return None
