#!/usr/bin/env python3
"""
HTMX Spring Boot Project Scaffolder

Creates a new Spring Boot + HTMX project structure with:
- Controller skeleton with HTMX endpoints
- Base layout template with HTMX + CSRF setup
- Example fragments (form, table, modal)
- Maven pom.xml with required dependencies
- Application properties configuration

Usage:
    python init_project.py --name my-app --package com.example.myapp
    python init_project.py --name todo-app --package com.company.todo --output ../projects
"""

import argparse
import sys
from pathlib import Path
from typing import Dict


TEMPLATES = {
    'pom.xml': '''<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
         https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.15</version>
        <relativePath/>
    </parent>
    
    <groupId>{group_id}</groupId>
    <artifactId>{artifact_id}</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>{project_name}</name>
    <description>Spring Boot + HTMX Application</description>
    
    <properties>
        <java.version>17</java.version>
    </properties>
    
    <dependencies>
        <!-- Spring Boot Starters -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        
        <!-- HTMX Integration -->
        <dependency>
            <groupId>io.github.wimdeblauwe</groupId>
            <artifactId>htmx-spring-boot</artifactId>
            <version>3.6.2</version>
        </dependency>
        <dependency>
            <groupId>io.github.wimdeblauwe</groupId>
            <artifactId>htmx-spring-boot-thymeleaf</artifactId>
            <version>3.6.2</version>
        </dependency>
        
        <!-- Development Tools -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        
        <!-- Testing -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>
''',

    'application.properties': '''# Application
spring.application.name={artifact_id}
server.port=8080

# Thymeleaf
spring.thymeleaf.cache=false
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html

# DevTools
spring.devtools.restart.enabled=true

# Security (customize for production)
spring.security.user.name=admin
spring.security.user.password=admin
''',

    'ItemController.java': '''package {package_name}.controller;

import io.github.wimdeblauwe.htmx.spring.boot.mvc.HtmxRequest;
import io.github.wimdeblauwe.htmx.spring.boot.mvc.HtmxResponse;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;

import javax.validation.Valid;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

@Controller
@RequestMapping("/items")
public class ItemController {{
    
    private final Map<Long, Item> items = new ConcurrentHashMap<>();
    private final AtomicLong counter = new AtomicLong(0);
    
    public ItemController() {{
        // Add sample data
        items.put(1L, new Item(1L, "Sample Item 1"));
        items.put(2L, new Item(2L, "Sample Item 2"));
        counter.set(2L);
    }}
    
    @GetMapping
    public String index(Model model) {{
        model.addAttribute("items", new ArrayList<>(items.values()));
        model.addAttribute("form", new ItemForm());
        return "item-list";
    }}
    
    @HxRequest
    @GetMapping
    public String listItems(Model model) {{
        model.addAttribute("items", new ArrayList<>(items.values()));
        return "fragments/item-table";
    }}
    
    @HxRequest
    @PostMapping
    public HtmxResponse createItem(@Valid @ModelAttribute ItemForm form,
                                   BindingResult result,
                                   Model model) {{
        if (result.hasErrors()) {{
            model.addAttribute("form", form);
            return HtmxResponse.builder()
                .view("fragments/item-form")
                .build();
        }}
        
        long id = counter.incrementAndGet();
        Item item = new Item(id, form.getName());
        items.put(id, item);
        
        model.addAttribute("item", item);
        model.addAttribute("count", items.size());
        
        return HtmxResponse.builder()
            .view(new ModelAndView("fragments/item-row", Map.of("item", item)))
            .trigger("item-created")
            .build();
    }}
    
    @HxRequest
    @GetMapping("/{{id}}")
    public String getItem(@PathVariable Long id, Model model) {{
        Item item = items.get(id);
        if (item == null) {{
            return "fragments/error";
        }}
        model.addAttribute("item", item);
        return "fragments/item-details";
    }}
    
    @HxRequest
    @DeleteMapping("/{{id}}")
    public HtmxResponse deleteItem(@PathVariable Long id) {{
        items.remove(id);
        
        return HtmxResponse.builder()
            .trigger("item-deleted")
            .build();
    }}
    
    // Inner classes
    public static class Item {{
        private Long id;
        private String name;
        
        public Item(Long id, String name) {{
            this.id = id;
            this.name = name;
        }}
        
        public Long getId() {{ return id; }}
        public void setId(Long id) {{ this.id = id; }}
        public String getName() {{ return name; }}
        public void setName(String name) {{ this.name = name; }}
    }}
    
    public static class ItemForm {{
        private String name;
        
        public String getName() {{ return name; }}
        public void setName(String name) {{ this.name = name; }}
    }}
}}
''',

    'SecurityConfig.java': '''package {package_name}.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
public class SecurityConfig {{
    
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {{
        http
            .authorizeHttpRequests(auth -> auth
                .antMatchers("/css/**", "/js/**", "/images/**").permitAll()
                .anyRequest().authenticated()
            )
            .formLogin(form -> form
                .loginPage("/login")
                .permitAll()
            )
            .logout(logout -> logout
                .permitAll()
            )
            .csrf(csrf -> csrf
                .ignoringAntMatchers("/h2-console/**") // If using H2 console
            );
        
        return http.build();
    }}
}}
''',

    'Application.java': '''package {package_name};

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {{
    
    public static void main(String[] args) {{
        SpringApplication.run(Application.class, args);
    }}
}}
''',

    'base.html': '''<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title th:text="${title ?: 'HTMX App'}">HTMX App</title>
    
    <!-- CSRF Meta Tags -->
    <meta name="_csrf" th:content="${{_csrf.token}}"/>
    <meta name="_csrf_header" th:content="${{_csrf.headerName}}"/>
    
    <!-- Bootstrap CSS -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
    
    <!-- HTMX -->
    <script src="https://unpkg.com/htmx.org@2.0.4"></script>
</head>
<body>
    <nav class="navbar navbar-expand-lg navbar-dark bg-dark">
        <div class="container">
            <a class="navbar-brand" href="/">HTMX App</a>
        </div>
    </nav>
    
    <div class="container mt-4">
        <div th:replace="${{~{{content}}}}"></div>
    </div>
    
    <!-- CSRF Configuration for HTMX -->
    <script>
        document.addEventListener('DOMContentLoaded', function() {{
            document.body.addEventListener('htmx:configRequest', function(evt) {{
                const token = document.querySelector('meta[name="_csrf"]').getAttribute('content');
                const header = document.querySelector('meta[name="_csrf_header"]').getAttribute('content');
                evt.detail.headers[header] = token;
            }});
        }});
    </script>
</body>
</html>
''',

    'item-list.html': '''<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
      th:replace="~{{layout/base :: layout(~{{::content}})}}">
<head>
    <title>Item List</title>
</head>
<body>
    <th:block th:fragment="content">
        <h1>Items</h1>
        
        <div class="row">
            <div class="col-md-8">
                <div id="item-list">
                    <div th:replace="~{{fragments/item-table}}"></div>
                </div>
            </div>
            
            <div class="col-md-4">
                <h3>Add New Item</h3>
                <div th:replace="~{{fragments/item-form}}"></div>
            </div>
        </div>
    </th:block>
</body>
</html>
''',

    'item-table.html': '''<div th:fragment="item-table">
    <table class="table">
        <thead>
            <tr>
                <th>ID</th>
                <th>Name</th>
                <th>Actions</th>
            </tr>
        </thead>
        <tbody id="item-tbody">
            <tr th:each="item : ${{items}}" th:fragment="item-row">
                <td th:text="${{item.id}}">1</td>
                <td th:text="${{item.name}}">Sample Item</td>
                <td>
                    <button class="btn btn-sm btn-danger"
                            hx:delete="@{{/items/{{id}}(id=${{item.id}})}}"
                            hx-target="closest tr"
                            hx-swap="outerHTML swap:0.5s"
                            hx-confirm="Delete this item?">
                        Delete
                    </button>
                </td>
            </tr>
        </tbody>
    </table>
</div>
''',

    'item-form.html': '''<form th:fragment="item-form"
      hx-post="/items"
      hx-target="#item-tbody"
      hx-swap="beforeend"
      hx-on::after-request="this.reset()">
    
    <div class="mb-3">
        <label for="name" class="form-label">Item Name</label>
        <input type="text"
               class="form-control"
               id="name"
               name="name"
               required
               th:field="*{{form.name}}"
               th:classappend="${{#fields.hasErrors('name')}} ? 'is-invalid'">
        <div class="invalid-feedback" th:if="${{#fields.hasErrors('name')}}" th:errors="*{{form.name}}">
            Name is required
        </div>
    </div>
    
    <button type="submit" class="btn btn-primary">Add Item</button>
</form>
''',
}


def create_project(project_name: str, package_name: str, output_dir: Path):
    """Generate Spring Boot + HTMX project structure"""
    
    # Parse package name
    package_parts = package_name.split('.')
    group_id = '.'.join(package_parts[:-1]) if len(package_parts) > 1 else package_name
    artifact_id = package_parts[-1] if len(package_parts) > 1 else project_name
    
    # Create directory structure
    project_dir = output_dir / project_name
    src_main = project_dir / 'src' / 'main'
    src_test = project_dir / 'src' / 'test'
    
    java_main = src_main / 'java' / '/'.join(package_parts)
    java_test = src_test / 'java' / '/'.join(package_parts)
    resources = src_main / 'resources'
    templates = resources / 'templates'
    fragments = templates / 'fragments'
    layout = templates / 'layout'
    static = resources / 'static'
    
    # Create all directories
    for directory in [java_main / 'controller', java_main / 'config', java_test,
                     fragments, layout, static / 'css', static / 'js']:
        directory.mkdir(parents=True, exist_ok=True)
    
    # Template variables
    vars = {
        'project_name': project_name,
        'package_name': package_name,
        'group_id': group_id,
        'artifact_id': artifact_id,
    }
    
    # Write files
    files_created = []
    
    # Root files
    (project_dir / 'pom.xml').write_text(TEMPLATES['pom.xml'].format(**vars))
    files_created.append('pom.xml')
    
    # Resources
    (resources / 'application.properties').write_text(TEMPLATES['application.properties'].format(**vars))
    files_created.append('src/main/resources/application.properties')
    
    # Java files
    (java_main / 'Application.java').write_text(TEMPLATES['Application.java'].format(**vars))
    files_created.append(f'src/main/java/{"/".join(package_parts)}/Application.java')
    
    (java_main / 'controller' / 'ItemController.java').write_text(TEMPLATES['ItemController.java'].format(**vars))
    files_created.append(f'src/main/java/{"/".join(package_parts)}/controller/ItemController.java')
    
    (java_main / 'config' / 'SecurityConfig.java').write_text(TEMPLATES['SecurityConfig.java'].format(**vars))
    files_created.append(f'src/main/java/{"/".join(package_parts)}/config/SecurityConfig.java')
    
    # Templates
    (layout / 'base.html').write_text(TEMPLATES['base.html'])
    files_created.append('src/main/resources/templates/layout/base.html')
    
    (templates / 'item-list.html').write_text(TEMPLATES['item-list.html'])
    files_created.append('src/main/resources/templates/item-list.html')
    
    (fragments / 'item-table.html').write_text(TEMPLATES['item-table.html'])
    files_created.append('src/main/resources/templates/fragments/item-table.html')
    
    (fragments / 'item-form.html').write_text(TEMPLATES['item-form.html'])
    files_created.append('src/main/resources/templates/fragments/item-form.html')
    
    # Create README
    readme_content = f'''# {project_name}

Spring Boot + HTMX Application

## Generated Structure

- **Controllers**: ItemController with CRUD endpoints
- **Templates**: Base layout with HTMX + CSRF setup
- **Fragments**: Reusable item table and form
- **Security**: Spring Security configuration

## Running the Application

```bash
./mvnw spring-boot:run
```

Visit: http://localhost:8080

Default credentials:
- Username: admin
- Password: admin

## Project Structure

```
{project_name}/
├── src/main/java/{package_name.replace(".", "/")}/
│   ├── Application.java
│   ├── controller/
│   │   └── ItemController.java
│   └── config/
│       └── SecurityConfig.java
└── src/main/resources/
    ├── templates/
    │   ├── layout/base.html
    │   ├── fragments/
    │   │   ├── item-table.html
    │   │   └── item-form.html
    │   └── item-list.html
    └── application.properties
```

## Next Steps

1. Customize SecurityConfig for your authentication needs
2. Add database integration (JPA/JDBC)
3. Create additional controllers and templates
4. Add custom styling in static/css/
5. Run tests with `./mvnw test`
'''
    
    (project_dir / 'README.md').write_text(readme_content)
    files_created.append('README.md')
    
    return project_dir, files_created


def main():
    parser = argparse.ArgumentParser(description='Create a new Spring Boot + HTMX project')
    parser.add_argument('--name', required=True, help='Project name')
    parser.add_argument('--package', required=True, help='Java package name (e.g., com.example.myapp)')
    parser.add_argument('--output', default='.', help='Output directory (default: current directory)')
    
    args = parser.parse_args()
    
    output_dir = Path(args.output).resolve()
    
    if not output_dir.exists():
        print(f"Error: Output directory does not exist: {output_dir}")
        sys.exit(1)
    
    print(f"Creating Spring Boot + HTMX project: {args.name}")
    print(f"Package: {args.package}")
    print(f"Output: {output_dir}\n")
    
    try:
        project_dir, files = create_project(args.name, args.package, output_dir)
        
        print(f"✓ Project created successfully at: {project_dir}\n")
        print(f"Files created ({len(files)}):")
        for file in files:
            print(f"  - {file}")
        
        print(f"\nNext steps:")
        print(f"  cd {project_dir}")
        print(f"  ./mvnw spring-boot:run")
        print(f"\nThen visit: http://localhost:8080")
        
    except Exception as e:
        print(f"Error creating project: {e}")
        sys.exit(1)


if __name__ == '__main__':
    main()
