"""
Canvas content downloader.
"""

import requests
import json
from pathlib import Path


class CanvasDownloader:
    """Downloads all content from a Canvas course."""

    def __init__(self, canvas_token: str, course_id: str, base_url: str = "https://canvas.tue.nl/api/v1"):
        self.canvas_token = canvas_token
        self.course_id = course_id
        self.base_url = base_url
        self.headers = {"Authorization": f"Bearer {canvas_token}"}

    def verify_token(self) -> bool:
        url = f"{self.base_url}/users/self/profile"
        response = requests.get(url, headers=self.headers)
        if response.status_code == 401:
            print("Error: Invalid or expired token.")
            return False
        response.raise_for_status()
        profile = response.json()
        print(f"Authenticated as: {profile.get('name')} ({profile.get('login_id')})")
        return True

    def _paginated_get(self, url: str) -> list:
        """Fetch all pages of a paginated API endpoint."""
        items = []
        while url:
            response = requests.get(url, headers=self.headers)
            response.raise_for_status()
            items.extend(response.json())

            # Handle pagination
            links = response.headers.get("Link", "")
            url = None
            for link in links.split(","):
                if 'rel="next"' in link:
                    url = link.split(";")[0].strip("<> ")
                    break
        return items

    def get_modules(self) -> list:
        """Fetch all modules for the course."""
        url = f"{self.base_url}/courses/{self.course_id}/modules"
        return self._paginated_get(url)

    def get_module_items(self, module_id: int) -> list:
        """Fetch all items in a module."""
        url = f"{self.base_url}/courses/{self.course_id}/modules/{module_id}/items"
        return self._paginated_get(url)

    def get_page_content(self, page_url: str) -> dict:
        """Fetch content of a page."""
        response = requests.get(page_url, headers=self.headers)
        response.raise_for_status()
        return response.json()

    def get_file_info(self, file_id: int) -> dict:
        """Get file information including download URL."""
        url = f"{self.base_url}/files/{file_id}"
        response = requests.get(url, headers=self.headers)
        response.raise_for_status()
        return response.json()

    def download_file(self, file_url: str, filename: str, folder: Path) -> str:
        """Download a file to the specified folder."""
        folder.mkdir(parents=True, exist_ok=True)

        response = requests.get(file_url, headers=self.headers, stream=True)
        response.raise_for_status()

        filepath = folder / filename
        with open(filepath, "wb") as f:
            for chunk in response.iter_content(chunk_size=8192):
                f.write(chunk)

        return str(filepath)

    def download_course(self, output_dir: Path) -> dict:
        """Download all course content to the specified directory."""
        output_dir.mkdir(exist_ok=True)

        print(f"Fetching modules for Course ID: {self.course_id}...")
        modules = self.get_modules()

        course_content = {"course_id": self.course_id, "modules": []}

        for module in modules:
            print(f"\n--- Module: {module['name']} ---")
            module_data = {
                "id": module["id"],
                "name": module["name"],
                "position": module.get("position"),
                "items": [],
            }

            # Create folder for module
            module_folder = output_dir / f"{module['position']:02d}_{self._sanitize_filename(module['name'][:50])}"
            module_folder.mkdir(exist_ok=True)

            items = self.get_module_items(module["id"])

            for item in items:
                print(f"  - {item['type']}: {item['title']}")

                item_data = {
                    "id": item["id"],
                    "title": item["title"],
                    "type": item["type"],
                    "position": item.get("position"),
                }

                if item["type"] == "Page":
                    if "url" in item:
                        page_url = f"{self.base_url}/courses/{self.course_id}/pages/{item['page_url']}"
                        page = self.get_page_content(page_url)
                        item_data["body"] = page.get("body", "")
                        page_file = module_folder / f"{self._sanitize_filename(item['title'][:50])}.html"
                        with open(page_file, "w", encoding="utf-8") as f:
                            f.write(f"<h1>{item['title']}</h1>\n{page.get('body', '')}")

                elif item["type"] == "File":
                    if "content_id" in item:
                        file_info = self.get_file_info(item["content_id"])
                        item_data["filename"] = file_info.get("display_name")
                        item_data["size"] = file_info.get("size")
                        download_url = file_info.get("url")
                        if download_url:
                            filepath = self.download_file(download_url, file_info["display_name"], module_folder)
                            item_data["local_path"] = filepath
                            print(f"    Downloaded: {file_info['display_name']}")

                elif item["type"] == "ExternalUrl":
                    item_data["external_url"] = item.get("external_url")

                elif item["type"] == "Assignment":
                    if "content_id" in item:
                        assignment_url = f"{self.base_url}/courses/{self.course_id}/assignments/{item['content_id']}"
                        response = requests.get(assignment_url, headers=self.headers)
                        if response.status_code == 200:
                            assignment = response.json()
                            item_data["description"] = assignment.get("description", "")
                            item_data["due_at"] = assignment.get("due_at")

                module_data["items"].append(item_data)

            course_content["modules"].append(module_data)

        # Save complete course structure as JSON
        with open(output_dir / "course_structure.json", "w", encoding="utf-8") as f:
            json.dump(course_content, f, indent=2, ensure_ascii=False)

        print(f"\n\nDone! Content saved to: {output_dir.absolute()}")
        print(f"Course structure saved to: {output_dir / 'course_structure.json'}")

        return course_content

    @staticmethod
    def _sanitize_filename(name: str) -> str:
        """Remove or replace characters that are invalid in filenames."""
        return name.replace("/", "_").replace(":", "_").replace("\\", "_").replace("*", "_").replace("?", "_").replace('"', "_").replace("<", "_").replace(">", "_").replace("|", "_")
