"""Markdown documentation generator"""

import logging
from datetime import datetime
from typing import Any, Dict

from jinja2 import Template


class MarkdownGenerator:
    """Generate Markdown documentation from extracted metadata"""

    def __init__(self):
        self.logger = logging.getLogger(__name__)

    def generate(self, metadata: Dict[str, Any]) -> str:
        """Generate Markdown documentation from metadata"""
        self.logger.debug("Generating Markdown documentation")

        # Determine file type and use appropriate template
        file_type = metadata.get("type", "Unknown")

        if file_type == "Power BI":
            content = self._generate_powerbi_markdown(metadata)
        elif file_type == "Tableau":
            content = self._generate_tableau_markdown(metadata)
        else:
            content = self._generate_generic_markdown(metadata)

        # Clean up markdown formatting issues
        return self._clean_markdown(content)

    def _generate_powerbi_markdown(self, metadata: Dict[str, Any]) -> str:
        """Generate Markdown for Power BI files"""
        template = Template(
            """# Documentation for {{ file_name }}

Generated on {{ generation_date }}

## Overview

**File Type:** {{ file_type }}
**File Path:** `{{ file_path }}`

{% if ai_summary %}
## AI Summary

{{ ai_summary }}

{% endif %}
## Data Sources

{% for ds in data_sources %}
### {{ ds.name }}

- **Type:** {{ ds.type }}
- **Connection:** {{ ds.connection }}

{% endfor %}

## Tables and Fields

{% for table in tables %}
### {{ table.name }}

| Field Name | Data Type | Description |
|------------|-----------|-------------|
{% for column in table.columns %}| `{{ column.name }}` | {{ column.data_type }} | {{ column.description or '' }}{% if column.is_hidden %} *(Hidden)*{% endif %} |
{% endfor %}

{% if table.row_count %}**Estimated Rows:** {{ table.row_count }}{% endif %}

{% endfor %}

## Measures

{% for measure in measures %}
### {{ measure.name }}

**Table:** {{ measure.table or 'not available' }}

**Expression:**

```dax
{{ measure.expression_formatted if measure.expression_formatted != 'not available' else measure.expression }}
```

{% if measure.format_string and measure.format_string != 'not available' %}**Format String:** {{ measure.format_string }}{% endif %}
{% if measure.description and measure.description != 'not available' %}**Description:** {{ measure.description }}{% else %}**Description:** not available{% endif %}
{% if measure.display_folder and measure.display_folder != 'not available' %}**Display Folder:** {{ measure.display_folder }}{% endif %}
{% if measure.is_hidden %}**Hidden:** Yes{% endif %}
{% if measure.data_type and measure.data_type != 'not available' %}**Data Type:** {{ measure.data_type }}{% endif %}

---

{% endfor %}

## Calculated Columns

{% for column in calculated_columns %}
### {{ column.name }}

**Table:** {{ column.table or 'not available' }}
**Data Type:** {{ column.data_type or 'not available' }}

**Expression:**

```dax
{{ column.expression_formatted if column.expression_formatted != 'not available' else column.expression }}
```

{% if column.description and column.description != 'not available' %}**Description:** {{ column.description }}{% else %}**Description:** not available{% endif %}
{% if column.format_string and column.format_string != 'not available' %}**Format String:** {{ column.format_string }}{% endif %}
{% if column.display_folder and column.display_folder != 'not available' %}**Display Folder:** {{ column.display_folder }}{% endif %}
{% if column.is_hidden %}**Hidden:** Yes{% endif %}

---

{% endfor %}

## Relationships

{% for rel in relationships %}
- **{{ rel.from_table }}**[`{{ rel.from_column }}`] → **{{ rel.to_table }}**[`{{ rel.to_column }}`]
  - Cardinality: {{ rel.cardinality }}
  - Active: {{ 'Yes' if rel.is_active else 'No' }}
  - Cross Filter: {{ rel.cross_filter_direction }}

{% endfor %}

## Report Structure

{% for page in visualizations %}
### Page: {{ page.page }}

{% for visual in page.visuals %}
#### {{ visual.title or visual.type }}
- **Type:** {{ visual.type }}
{% if visual.fields %}
- **Fields Used:** {% for field in visual.fields %}`{{ field }}`{% if not loop.last %}, {% endif %}{% endfor %}
{% endif %}

{% endfor %}
{% endfor %}

{% if power_query %}
## Power Query (M Code)

{% for query_name, query_code in power_query.items() %}
### {{ query_name }}

```m
{{ query_code }}
```

{% endfor %}
{% endif %}

---
Documentation generated by BI Documentation Tool
"""
        )

        return template.render(
            file_name=metadata.get("file", "Unknown"),
            file_type=metadata.get("type", "Unknown"),
            file_path=metadata.get("file_path", ""),
            generation_date=datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
            ai_summary=metadata.get("ai_summary", ""),
            data_sources=metadata.get("data_sources", []),
            tables=metadata.get("tables", []),
            measures=metadata.get("measures", []),
            calculated_columns=metadata.get("calculated_columns", []),
            relationships=metadata.get("relationships", []),
            visualizations=metadata.get("visualizations", []),
            power_query=metadata.get("power_query", {}),
        )

    def _generate_tableau_markdown(self, metadata: Dict[str, Any]) -> str:
        """Generate Markdown for Tableau files"""
        template = Template(
            """# Documentation for {{ file_name }}

Generated on {{ generation_date }}

## Overview

**File Type:** {{ file_type }}
**File Path:** `{{ file_path }}`

{% if ai_summary %}
## AI Summary

{{ ai_summary }}

{% endif %}
## Data Sources

{% for ds in data_sources %}
### {{ ds.name }}

{% if ds.caption != ds.name %}**Caption:** {{ ds.caption }}{% endif %}

#### Connections
{% for conn in ds.connections %}
- **Type:** {{ conn.connection_type }}
- **Server:** {{ conn.server }}
- **Database:** {{ conn.database }}
{% if conn.port %}- **Port:** {{ conn.port }}{% endif %}
{% if conn.username %}- **Username:** {{ conn.username }}{% endif %}

{% endfor %}

#### Fields

| Field Name | Caption | Data Type | Role | Type | Calculated |
|------------|---------|-----------|------|------|------------|
{% for field in ds.fields %}| `{{ field.name }}` | {{ field.caption }} | {{ field.datatype }} | {{ field.role }} | {{ field.type }} | {{ 'Yes' if field.is_calculated else 'No' }} |
{% endfor %}

{% endfor %}

## Calculated Fields

{% for field in calculated_fields %}
### {{ field.name }}

**Data Source:** {{ field.datasource or 'not available' }}
**Data Type:** {{ field.datatype or 'not available' }}
**Role:** {{ field.role or 'not available' }}

**Calculation:**

```sql
{{ field.calculation_formatted if field.calculation_formatted != 'not available' else field.calculation }}
```

{% if field.description and field.description != 'not available' %}**Description:** {{ field.description }}{% else %}**Description:** not available{% endif %}
{% if field.folder and field.folder != 'not available' %}**Folder:** {{ field.folder }}{% endif %}
{% if field.is_hidden %}**Hidden:** Yes{% endif %}

{% if field.worksheets_used %}
**Used in Worksheets:** {% for ws in field.worksheets_used %}`{{ ws }}`{% if not loop.last %}, {% endif %}{% endfor %}
{% endif %}

---

{% endfor %}

## Worksheets

{% for worksheet in worksheets %}
### {{ worksheet.name }}

{% if worksheet.data_source %}**Data Source:** {{ worksheet.data_source }}{% endif %}

{% if worksheet.fields_used %}
**Fields Used:** {% for field in worksheet.fields_used %}`{{ field }}`{% if not loop.last %}, {% endif %}{% endfor %}
{% endif %}

{% if worksheet.filters %}
**Filters:**
{% for filter in worksheet.filters %}
- {{ filter.field }}: {{ filter.value }}
{% endfor %}
{% endif %}

{% if worksheet.parameters_used %}
**Parameters Used:** {% for param in worksheet.parameters_used %}`{{ param }}`{% if not loop.last %}, {% endif %}{% endfor %}
{% endif %}

{% endfor %}

## Dashboards

{% for dashboard in dashboards %}
### {{ dashboard.name }}

**Contains Worksheets:** {% for ws in dashboard.worksheets %}`{{ ws }}`{% if not loop.last %}, {% endif %}{% endfor %}

{% if dashboard.objects %}
**Dashboard Objects:**
{% for obj in dashboard.objects %}
- {{ obj.type }}: {{ obj.name }}
{% endfor %}
{% endif %}

{% endfor %}

{% if parameters %}
## Parameters

{% for param in parameters %}
### {{ param.name }}

- **Data Type:** {{ param.datatype }}
{% if param.default_value %}- **Default Value:** {{ param.default_value }}{% endif %}
{% if param.allowable_values %}- **Allowed Values:** {% for val in param.allowable_values %}`{{ val }}`{% if not loop.last %}, {% endif %}{% endfor %}{% endif %}
{% if param.description %}- **Description:** {{ param.description }}{% endif %}

{% endfor %}
{% endif %}

{% if field_usage %}
## Field Usage Summary

| Field | Used in Worksheets |
|-------|-------------------|
{% for field, worksheets in field_usage.items() %}| `{{ field }}` | {% for ws in worksheets %}`{{ ws }}`{% if not loop.last %}, {% endif %}{% endfor %} |
{% endfor %}
{% endif %}

---
Documentation generated by BI Documentation Tool
"""
        )

        return template.render(
            file_name=metadata.get("file", "Unknown"),
            file_type=metadata.get("type", "Unknown"),
            file_path=metadata.get("file_path", ""),
            generation_date=datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
            ai_summary=metadata.get("ai_summary", ""),
            data_sources=metadata.get("data_sources", []),
            calculated_fields=metadata.get("calculated_fields", []),
            worksheets=metadata.get("worksheets", []),
            dashboards=metadata.get("dashboards", []),
            parameters=metadata.get("parameters", []),
            field_usage=metadata.get("field_usage", {}),
        )

    def _generate_generic_markdown(self, metadata: Dict[str, Any]) -> str:
        """Generate generic Markdown for unknown file types"""
        template = Template(
            """# Documentation for {{ file_name }}

Generated on {{ generation_date }}

## Overview

**File Type:** {{ file_type }}
**File Path:** `{{ file_path }}`

## Raw Metadata

```json
{{ metadata_json }}
```

---
Documentation generated by BI Documentation Tool
"""
        )

        import json

        return template.render(
            file_name=metadata.get("file", "Unknown"),
            file_type=metadata.get("type", "Unknown"),
            file_path=metadata.get("file_path", ""),
            generation_date=datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
            metadata_json=json.dumps(metadata, indent=2, default=str),
        )

    def _clean_markdown(self, content: str) -> str:
        """Clean up markdown formatting issues to pass markdownlint"""
        import re

        # First, split into lines for easier processing
        lines = content.split("\n")

        # Remove trailing spaces from all lines (MD009)
        lines = [line.rstrip() for line in lines]

        # Remove any hard tabs and replace with spaces (MD010)
        lines = [line.replace("\t", "    ") for line in lines]

        # Fix code span issues by removing extra spaces (MD038)
        # Handle multiple patterns: spaces at start, end, or both
        for i, original_line in enumerate(lines):
            # Remove spaces at the beginning of code spans
            updated_line = re.sub(r"`\s+([^`]+)`", r"`\1`", original_line)
            # Remove spaces at the end of code spans
            updated_line = re.sub(r"`([^`]+)\s+`", r"`\1`", updated_line)
            # Remove spaces at both ends
            updated_line = re.sub(r"`\s+([^`]+)\s+`", r"`\1`", updated_line)
            lines[i] = updated_line

        # Handle long lines by wrapping at 80 characters where possible (MD013)
        wrapped_lines = []
        for line in lines:
            if (
                len(line) <= 80
                or line.strip().startswith("|")
                or line.strip().startswith("```")
            ):
                # Don't wrap table rows, code blocks, or already short lines
                wrapped_lines.append(line)
            elif line.strip().startswith("- **") and ": " in line:
                # Wrap list items with key-value pairs
                if len(line) > 80:
                    indent = len(line) - len(line.lstrip())
                    parts = line.split(": ", 1)
                    if len(parts) == 2 and len(parts[0]) < 60:
                        wrapped_lines.append(parts[0] + ":")
                        wrapped_lines.append(" " * (indent + 2) + parts[1])
                    else:
                        wrapped_lines.append(line)
                else:
                    wrapped_lines.append(line)
            else:
                wrapped_lines.append(line)

        lines = wrapped_lines

        # Track headings to fix duplicate issues (MD024)
        seen_headings = {}
        for i, line in enumerate(lines):
            if line.startswith("#"):
                # Extract heading text (remove # and strip)
                heading_text = line.lstrip("#").strip()
                if heading_text in seen_headings:
                    # Make heading unique by adding context
                    seen_headings[heading_text] += 1
                    # Add section number or parent context
                    level = len(line) - len(line.lstrip("#"))
                    if level > 1:  # Sub-heading
                        lines[
                            i
                        ] = f"{'#' * level} {heading_text} ({seen_headings[heading_text]})"
                else:
                    seen_headings[heading_text] = 1

        # Rejoin content
        content = "\n".join(lines)

        # More aggressive blank line removal (MD012)
        # Remove any occurrence of 3+ consecutive newlines
        content = re.sub(r"\n{3,}", "\n\n", content)

        # Ensure fenced code blocks are surrounded by blank lines (MD031)
        # Split content to handle code blocks more carefully
        parts = []
        current_part = ""
        in_code_block = False

        for line in content.split("\n"):
            if line.strip().startswith("```"):
                if not in_code_block:
                    # Starting a code block
                    if current_part and not current_part.endswith("\n\n"):
                        current_part = current_part.rstrip() + "\n\n"
                    current_part += line + "\n"
                    in_code_block = True
                else:
                    # Ending a code block
                    current_part += line + "\n\n"
                    in_code_block = False
            elif in_code_block:
                current_part += line + "\n"
            elif line.strip() == "":
                current_part += "\n"
            else:
                current_part += line + "\n"

        content = current_part

        # Clean up multiple blank lines again after code block processing
        content = re.sub(r"\n{3,}", "\n\n", content)

        # Remove blank lines at the very beginning
        content = content.lstrip("\n")

        # Ensure file ends with exactly one newline (MD047)
        content = content.rstrip() + "\n"

        # Final cleanup of any remaining issues
        # Remove trailing spaces that might have been reintroduced
        lines = content.split("\n")
        lines = [line.rstrip() for line in lines]
        content = "\n".join(lines)

        # Ensure exactly one final newline
        if not content.endswith("\n"):
            content += "\n"
        elif content.endswith("\n\n"):
            content = content.rstrip("\n") + "\n"

        return content
