import json
from typing import List

from fhir.resources.bundle import Bundle
from fhir.resources.reference import Reference
from fhir.resources.resource import Resource
from httpx import Response


class CreateResponse:
    @staticmethod
    def _process_location_header(server_create_response: dict):
        location_header: str = server_create_response.get(
            "Location", server_create_response.get("location")
        )
        if not location_header:
            raise ValueError("Location field not found.")
        split_location = location_header.split("/")
        location = "/".join(split_location[:-2])
        version = int(split_location[-1])
        resource_id = split_location[-3]
        return resource_id, location, version


class ResourceCreateResponse(CreateResponse):
    location: str = None
    resource: Resource = None
    version: int = None
    resource_id: str = None
    reference: Reference = None

    def __init__(self, server_response_dict: dict, resource: Resource):
        self.resource = resource
        resource_id, location, version = self._process_location_header(
            server_response_dict
        )
        self.location = location
        self.resource_id = resource_id
        self.resource.id = resource_id
        self.reference = Reference(
            **{"reference": self.resource.get_resource_type() + "/" + resource_id}
        )

    def __repr__(self):
        return (
            f"<{self.__class__.__name__}(resource_id={self.resource_id}, location={self.location},"
            f" version={self.version})>"
        )


class BundleCreateResponse:
    create_responses: List[ResourceCreateResponse] = None

    def __init__(self, server_response: Response, bundle: Bundle):
        self.create_responses = []
        for i, entry in enumerate(server_response.json()["entry"]):
            resource = bundle.entry[i].resource
            create_response = ResourceCreateResponse(entry["response"], resource)
            self.create_responses.append(create_response)

    @property
    def resources(self):
        return [r.resource for r in self.create_responses]

    @property
    def references(self):
        return [r.reference for r in self.create_responses]

    def __repr__(self):
        return (
            f"BundleCreateResponse(create_responses={self.create_responses[0]}...{self.create_responses[-1]}, "
            f"num_resources={len(self.create_responses)})"
        )


class TransferResponse:
    origin_server: str
    destination_server: str
    create_responses: List[ResourceCreateResponse]
    linkage: dict
    n_transferred: int

    def __init__(
        self,
        origin_server: str,
        destination_server: str,
        create_responses: List[ResourceCreateResponse],
        linkage: dict = None,
    ):
        self.origin_server = origin_server
        self.destination_server = destination_server
        self.create_responses = create_responses
        self.linkage = linkage
        self.n_transferred = len(create_responses)

    def save_linkage(self, filename: str):
        with open(filename, "w") as f:
            json.dump(self.linkage, f)

    def __str__(self):
        return (
            f"TransferResponse origin({self.origin_server}) -> destination ({self.destination_server})\n"
            f"n_transferred: {self.n_transferred}, linkage: {'stored' if self.linkage else 'not stored'}"
        )

    def __repr__(self):
        return (
            f"<{self.__class__.__name__}(origin_server={self.origin_server},"
            f" destination_server={self.destination_server})"
        )


class UpdateResponse:
    # TODO: implement
    def __init__(self, server_response: Response):
        pass
