"""
SARC Publisher — FastAPI application
Migrated from Flask for async ASGI performance with uvicorn.
"""
import os
import re
import logging
from datetime import datetime
from urllib.parse import unquote
from pathlib import Path

from fastapi import FastAPI, Request, Depends, HTTPException, Query
from fastapi.responses import (
    HTMLResponse, RedirectResponse, FileResponse, Response, JSONResponse,
)
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from starlette.middleware.sessions import SessionMiddleware

from sqlalchemy import create_engine, Column, Integer, String, DateTime, Text, or_
from sqlalchemy.orm import DeclarativeBase, sessionmaker, Session

from werkzeug.security import generate_password_hash, check_password_hash
from werkzeug.utils import secure_filename

from starlette.requests import Request as StarletteRequest

# ─────────────────────────────────────────────────────────
# Flask-compatibility: make request.endpoint & request.view_args work in templates
# ─────────────────────────────────────────────────────────
@property
def _compat_endpoint(self):
    return self.scope.get("path", "")

@property
def _compat_view_args(self):
    return self.path_params

StarletteRequest.endpoint = _compat_endpoint
StarletteRequest.view_args = _compat_view_args

# ─────────────────────────────────────────────────────────
# Logging
# ─────────────────────────────────────────────────────────
logging.basicConfig(
    filename="debug.log", filemode="a",
    format="%(asctime)s,%(msecs)03d %(name)s %(levelname)s %(message)s",
    datefmt="%Y-%m-%d %H:%M:%S", level=logging.DEBUG,
)

# ─────────────────────────────────────────────────────────
# Constants
# ─────────────────────────────────────────────────────────
BASE_DIR = Path(__file__).resolve().parent
UPLOAD_FOLDER = str(BASE_DIR / "uploads")
ALLOWED_EXTENSIONS = {"txt", "pdf", "doc", "docx"}
ALLOWED_IMAGE_EXTENSIONS = {"jpg", "jpeg", "png", "webp"}
MAX_UPLOAD = 16 * 1024 * 1024

# ─────────────────────────────────────────────────────────
# Database
# ─────────────────────────────────────────────────────────
DATABASE_URL = os.environ.get("DATABASE_URL",'mysql+pymysql://tezloans_sarcouncil:Tamanna$1234#@localhost/tezloans_sarcouncil')
if DATABASE_URL:
    engine = create_engine(DATABASE_URL, pool_pre_ping=True, pool_recycle=25)
else:
    engine = create_engine(
        "sqlite:///" + str(BASE_DIR / "test.db"),
        connect_args={"check_same_thread": False},
    )

SessionLocal = sessionmaker(bind=engine, autoflush=False, expire_on_commit=False)


class Base(DeclarativeBase):
    pass


# ─────────────────────────────────────────────────────────
# Models
# ─────────────────────────────────────────────────────────
class Login(Base):
    __tablename__ = "login"
    id = Column(Integer, primary_key=True)
    username = Column(String(100), unique=True, nullable=False)
    password = Column(String(200), nullable=False)


class Article(Base):
    __tablename__ = "article"
    id = Column(Integer, primary_key=True)
    journal = Column(String(100))
    volume_number = Column(String(100))
    volume_year = Column(Integer)
    issue_number = Column(String(100))
    first_page = Column(String(100))
    last_page = Column(String(100))
    title = Column(String(200))
    author = Column(String(500))
    keywords = Column(String(500))
    abstract = Column(String(2000))
    file_path = Column(String(2000))
    publish_date = Column(DateTime)
    url = Column(String(200))
    doi = Column(String(200))

    def __repr__(self):
        return f"Title: {self.title}"


class Submissions(Base):
    __tablename__ = "submissions"
    id = Column(Integer, primary_key=True)
    journal = Column(String(100))
    title = Column(String(200))
    author = Column(String(100))
    author_email = Column(String(100))
    author_phone = Column(String(20))
    file_path = Column(String(2000))
    submit_date = Column(DateTime)


class Messages(Base):
    __tablename__ = "messages"
    id = Column(Integer, primary_key=True)
    name = Column(String(200))
    phone = Column(String(20))
    email = Column(String(100))
    message = Column(String(2000))
    submit_date = Column(DateTime)


class Eduvid(Base):
    __tablename__ = "eduvid"
    id = Column(Integer, primary_key=True)
    title = Column(String(200))
    media = Column(String(1000))
    content = Column(String(2000))
    publish_date = Column(DateTime)


class Editors(Base):
    __tablename__ = "editors"
    id = Column(Integer, primary_key=True)
    name = Column(String(200))
    journal = Column(String(100))
    image = Column(String(200))
    designation = Column(String(200))
    description = Column(String(1000))


class Reviewer(Base):
    __tablename__ = "reviewer"
    id = Column(Integer, primary_key=True)
    name = Column(String(200))
    journal = Column(String(100))
    image = Column(String(200))
    description = Column(String(1000))


class EditorRequest(Base):
    __tablename__ = "editor_request"
    id = Column(Integer, primary_key=True)
    name = Column(String(200))
    email = Column(String(100))
    phone = Column(String(200))
    journals = Column(String(1000))
    filepath = Column(String(1000))
    submit_date = Column(DateTime)


class Conference(Base):
    __tablename__ = "conference"
    id = Column(Integer, primary_key=True)
    title = Column(String(200))
    announcement = Column(Text)
    conference_details = Column(Text)
    poster = Column(String(200))
    brochure = Column(String(200))
    submit_date = Column(DateTime)


Base.metadata.create_all(bind=engine)

# ─────────────────────────────────────────────────────────
# FastAPI app
# ─────────────────────────────────────────────────────────
app = FastAPI(docs_url=None, redoc_url=None)
app.add_middleware(SessionMiddleware, secret_key="sarcouncil_secret_key")
app.mount("/static", StaticFiles(directory=str(BASE_DIR / "static")), name="static")

templates = Jinja2Templates(directory=str(BASE_DIR / "templates"))


# ─────────────────────────────────────────────────────────
# Template helpers
# ─────────────────────────────────────────────────────────
def slugify_text(value: str) -> str:
    return re.sub(r"[^a-zA-Z0-9\s]", "", value).replace(" ", "-").lower()

templates.env.filters["slugify_text"] = slugify_text


def _url_for_compat(endpoint, **kwargs):
    """Flask-compatible url_for() for Jinja2 templates."""
    if "year" in kwargs and "month" in kwargs and "url" in kwargs:
        return f"/{kwargs['year']}/{kwargs['month']}/{kwargs['url']}/"
    if isinstance(endpoint, str):
        if endpoint in ("index", "index2"):
            return "/"
        # When called as url_for(request.endpoint, **request.view_args),
        # endpoint is the current path (from monkey-patch) – just return it.
        if endpoint.startswith("/"):
            return endpoint
    return "/"

templates.env.globals["url_for"] = _url_for_compat


def slugify(text: str, title: str) -> str:
    if text == "":
        text = title
    text = text.lower()
    text = re.sub(r"[^\w\s-]", "", text).replace(" ", "-")
    text = re.sub(r"^-+|-+$|--+", "-", text).strip("-")
    return text


# ─────────────────────────────────────────────────────────
# Dependencies
# ─────────────────────────────────────────────────────────
def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()


def get_current_user(request: Request, db: Session) -> Login | None:
    uid = request.session.get("user_id")
    if uid is None:
        return None
    return db.query(Login).filter_by(id=uid).first()


def require_login(request: Request, db: Session) -> Login:
    user = get_current_user(request, db)
    if user is None:
        raise HTTPException(status_code=303, headers={"Location": "/my-account/"})
    return user


def allowed_file(fn: str) -> bool:
    return "." in fn and fn.rsplit(".", 1)[1].lower() in ALLOWED_EXTENSIONS

def allowed_video(fn: str) -> bool:
    return "." in fn and fn.rsplit(".", 1)[1].lower() == "mp4"

def allowed_image(fn: str) -> bool:
    return "." in fn and fn.rsplit(".", 1)[1].lower() in ALLOWED_IMAGE_EXTENSIONS


async def save_upload(upload, subfolder: str) -> str:
    filename = secure_filename(upload.filename)
    dest = os.path.join(UPLOAD_FOLDER, subfolder, filename)
    os.makedirs(os.path.dirname(dest), exist_ok=True)
    content = await upload.read()
    if len(content) > MAX_UPLOAD:
        raise HTTPException(status_code=413, detail="File too large")
    with open(dest, "wb") as f:
        f.write(content)
    return filename


# ─────────────────────────────────────────────────────────
# JOURNAL MAPPING
# ─────────────────────────────────────────────────────────
JOURNAL_MAP = {
    'journal of applied sciences|2945-3437': {'title': 'Sarcouncil Journal of Applied Sciences', 'abbrev': 'SJAS', 'slug': 'scientific-research-journal-of-applied-sciences', 'template_dir': 'sarcouncil-journal-of-applied-sciences'},
    'journal of arts and literature|2945-364x': {'title': 'Sarcouncil Journal of Arts and Literature', 'abbrev': 'SJAL', 'slug': 'sarcouncil-journal-arts-literature', 'template_dir': 'sarcouncil-journal-of-arts-and-literature'},
    'journal of agriculture|2945-3631': {'title': 'Sarcouncil Journal of Agriculture', 'abbrev': 'SJA', 'slug': 'sarcouncil-journal-of-agriculture', 'template_dir': 'sarcouncil-journal-of-agriculture'},
    'journal of arts humanities and social sciences|2945-3488': {'title': 'Sarcouncil Journal of Arts Humanities and Social Sciences', 'abbrev': 'SJAHSS', 'slug': 'scientific-research-journal-of-arts-humanities-and-social-sciences', 'template_dir': 'sarcouncil-journal-of-arts-humanities-and-social-sciences'},
    'journal of biomedical sciences|2945-3666': {'title': 'Sarcouncil Journal of Biomedical Sciences', 'abbrev': 'SJBS', 'slug': 'scientific-journal-of-biomedical-sciences', 'template_dir': 'sarcouncil-journal-of-biomedical-sciences'},
    'journal of economics and business management|2945-3593': {'title': 'Sarcouncil Journal of Economics and Business Management', 'abbrev': 'SJEBM', 'slug': 'scientific-research-journal-of-economics-and-business-management', 'template_dir': 'sarcouncil-journal-of-economics-and-business-management'},
    'journal of education and sociology|2945-3542': {'title': 'Sarcouncil Journal of Education and Sociology', 'abbrev': 'SJES', 'slug': 'scientific-research-journal-of-education-and-sociology', 'template_dir': 'sarcouncil-journal-of-education-and-sociology'},
    'journal of engineering and computer sciences|2945-3585': {'title': 'Sarcouncil Journal of Engineering and Computer Sciences', 'abbrev': 'SJECS', 'slug': 'scientific-research-journal-of-engineering-and-computer-sciences', 'template_dir': 'sarcouncil-journal-of-engineering-and-computer-sciences'},
    'journal of entrepreneurship and business management|2945-3720': {'title': 'Sarcouncil Journal of Entrepreneurship and Business Management', 'abbrev': 'SJEBM2', 'slug': 'scientific-research-journal-of-entrepreneurship-and-business-management', 'template_dir': 'sarcouncil-journal-of-entrepreneurship-and-business-management'},
    'journal of humanities and cultural studies|2945-3658': {'title': 'Sarcouncil Journal of Humanities and Cultural Studies', 'abbrev': 'SJHCS', 'slug': 'scientific-research-journal-of-humanities-studies', 'template_dir': 'sarcouncil-journal-of-humanities-studies'},
    'journal of internal medicine and public health|2945-3674': {'title': 'Sarcouncil Journal of Internal Medicine and Public Health', 'abbrev': 'SJIMPH', 'slug': 'scientific-research-journal-of-internal-medicine-and-public-health', 'template_dir': 'sarcouncil-journal-of-internal-medicine-and-public-health'},
    'journal of medical sciences|2945-3526': {'title': 'Sarcouncil Journal of Medical Sciences', 'abbrev': 'SJMS', 'slug': 'scientific-research-journal-of-medical-sciences', 'template_dir': 'sarcouncil-journal-of-medical-sciences'},
    'journal of medical series|2945-3550': {'title': 'Sarcouncil Journal of Medical Series', 'abbrev': 'SJMS2', 'slug': 'scientific-research-journal-of-medical-series', 'template_dir': 'sarcouncil-journal-of-medical-series'},
    'journal of medicine and surgery|2945-3534': {'title': 'Sarcouncil Journal of Medicine and Surgery', 'abbrev': 'SJMS3', 'slug': 'scientific-research-journal-of-medicine-surgery', 'template_dir': 'sarcouncil-journal-of-medicine-and-surgery'},
    'journal of multidisciplinary|2945-3445': {'title': 'Sarcouncil Journal of Multidisciplinary', 'abbrev': 'SJM', 'slug': 'scientific-research-journal-of-multidisciplinary-2', 'template_dir': 'sarcouncil-journal-of-multidisciplinary'},
    'journal of plant and agronomy|2980-4981': {'title': 'Sarcouncil Journal of Plant and Agronomy', 'abbrev': 'SJPA', 'slug': 'sarcouncil-journal-of-plant-and-agronomy', 'template_dir': 'sarcouncil-journal-of-plant-and-agronomy'},
    'journal of public administration and management|2945-3712': {'title': 'Sarcouncil Journal of Public Administration and Management', 'abbrev': 'SJPAM', 'slug': 'scientific-research-journal-of-public-administration-and-management', 'template_dir': 'sarcouncil-journal-of-public-administration-and-management'},
    'journal of economics intelligence and technology|3082-3994': {'title': 'Journal of Economics Intelligence and Technology', 'abbrev': 'JEIT', 'slug': 'journal-of-economics-intelligence-and-technology', 'template_dir': 'journal-of-economics-intelligence-and-technology'},
    'journal of innovative science|3082-4435': {'title': 'Journal of Innovative Science', 'abbrev': 'JIS', 'slug': 'journal-of-innovative-science', 'template_dir': 'journal-of-innovative-science'},
    'technology perception|3082-4451': {'title': 'Technology Perception', 'abbrev': 'TP', 'slug': 'technology-perception', 'template_dir': 'technology-perception'},
}


def get_journal_info(journal_key: str) -> dict:
    info = JOURNAL_MAP.get(journal_key.lower())
    if info:
        return info
    parts = journal_key.split("|")
    name = parts[0].strip() if parts else journal_key
    return {
        "title": name.title(),
        "abbrev": "".join(w[0].upper() for w in name.split()),
        "slug": name.replace(" ", "-").lower(),
        "template_dir": "sarcouncil-" + name.replace(" ", "-").lower(),
    }


def render(template_name: str, request: Request, **ctx):
    ctx["request"] = request
    return templates.TemplateResponse(template_name, ctx)


# ─────────────────────────────────────────────────────────
# Middleware: cache headers
# ─────────────────────────────────────────────────────────
@app.middleware("http")
async def add_cache_headers(request: Request, call_next):
    response = await call_next(request)
    path = request.url.path
    # Security headers (M7)
    response.headers["X-Content-Type-Options"] = "nosniff"
    response.headers["X-Frame-Options"] = "SAMEORIGIN"
    response.headers["Referrer-Policy"] = "strict-origin-when-cross-origin"
    if path.startswith("/wp-content/") or path.startswith("/wp-includes/"):
        response.headers["Cache-Control"] = "public, max-age=31536000"
    elif path.startswith("/download-article/"):
        response.headers["Cache-Control"] = "public, max-age=86400"
    elif path == "/sitemap.xml":
        response.headers["Cache-Control"] = "public, max-age=3600"
    elif "text/html" in response.headers.get("content-type", ""):
        response.headers["Cache-Control"] = "public, max-age=300"
    return response


# =====================================================================
# STATIC FILE ROUTES (wp-content/wp-includes served from /static)
# =====================================================================
@app.get("/wp-content/{path:path}")
async def serve_content(path: str):
    return FileResponse(str(BASE_DIR / "static" / "wp-content" / path))

@app.get("/wp-includes/{path:path}")
async def serve_includes(path: str):
    return FileResponse(str(BASE_DIR / "static" / "wp-includes" / path))

@app.get("/{year:int}/{month:int}/wp-content/{path:path}")
async def serve_content_ym(year: int, month: int, path: str):
    return FileResponse(str(BASE_DIR / "static" / "wp-content" / path))

@app.get("/{year:int}/{month:int}/wp-includes/{path:path}")
async def serve_includes_ym(year: int, month: int, path: str):
    return FileResponse(str(BASE_DIR / "static" / "wp-includes" / path))

@app.get("/{year:int}/wp-content/{path:path}")
async def serve_content_y(year: int, path: str):
    return FileResponse(str(BASE_DIR / "static" / "wp-content" / path))

@app.get("/{year:int}/wp-includes/{path:path}")
async def serve_includes_y(year: int, path: str):
    return FileResponse(str(BASE_DIR / "static" / "wp-includes" / path))


# =====================================================================
# ROBOTS.TXT
# =====================================================================
ROBOTS_TXT = """User-agent: *
Allow: /
Disallow: /admin-panel
Disallow: /submissions
Disallow: /editors
Disallow: /reviewer
Disallow: /new-article
Disallow: /new-editor
Disallow: /new-reviewer
Disallow: /edit-article
Disallow: /edit-editor
Disallow: /edit-reviewer
Disallow: /my-account
Disallow: /messages
Disallow: /issue
Disallow: /uploads/
Disallow: /query/
Disallow: /eduvid-content
Disallow: /new-eduvid
Disallow: /settings

Sitemap: https://sarcouncil.com/sitemap.xml
"""

@app.get("/robots.txt")
async def robots_txt():
    return Response(content=ROBOTS_TXT, media_type="text/plain")


# =====================================================================
# RSS & ATOM FEEDS
# =====================================================================
@app.get("/feed/")
async def feed_all(request: Request, db: Session = Depends(get_db)):
    articles = db.query(Article).order_by(Article.publish_date.desc()).limit(50).all()
    host_base = str(request.base_url).rstrip("/")
    return templates.TemplateResponse("public/feed.xml", {
        "request": request, "articles": articles,
        "journal_title": "SARC Publisher - All Journals",
        "journal_slug": "", "host_base": host_base,
    }, media_type="application/rss+xml")

@app.get("/feed/{journal_slug}/")
async def feed_journal(journal_slug: str, request: Request, db: Session = Depends(get_db)):
    host_base = str(request.base_url).rstrip("/")
    journal_key = None
    journal_title = journal_slug.replace("-", " ").title()
    for key, info in JOURNAL_MAP.items():
        if info["slug"] == journal_slug:
            journal_key = key
            journal_title = info["title"]
            break
    articles = (
        db.query(Article).filter_by(journal=journal_key)
        .order_by(Article.publish_date.desc()).limit(50).all()
        if journal_key else []
    )
    return templates.TemplateResponse("public/feed.xml", {
        "request": request, "articles": articles,
        "journal_title": journal_title, "journal_slug": journal_slug,
        "host_base": host_base,
    }, media_type="application/rss+xml")

@app.get("/feed/{journal_slug}/atom/")
async def feed_journal_atom(journal_slug: str, request: Request, db: Session = Depends(get_db)):
    host_base = str(request.base_url).rstrip("/")
    journal_key = None
    journal_title = journal_slug.replace("-", " ").title()
    for key, info in JOURNAL_MAP.items():
        if info["slug"] == journal_slug:
            journal_key = key
            journal_title = info["title"]
            break
    articles = (
        db.query(Article).filter_by(journal=journal_key)
        .order_by(Article.publish_date.desc()).limit(50).all()
        if journal_key else []
    )
    return templates.TemplateResponse("public/atom.xml", {
        "request": request, "articles": articles,
        "journal_title": journal_title, "journal_slug": journal_slug,
        "host_base": host_base,
    }, media_type="application/atom+xml")


# =====================================================================
# ARTICLES API
# =====================================================================
@app.get("/api/articles")
async def api_articles(journal: str = None, page: int = Query(1, ge=1), per_page: int = Query(10, ge=1, le=50), db: Session = Depends(get_db)):
    q = db.query(Article).order_by(Article.publish_date.desc())
    if journal:
        q = q.filter_by(journal=journal)
    articles = q.offset((page - 1) * per_page).limit(per_page).all()
    result = []
    for a in articles:
        info = get_journal_info(a.journal)
        result.append({
            "id": a.id, "title": a.title, "author": a.author,
            "keywords": a.keywords,
            "abstract": (a.abstract[:200] + "...") if a.abstract and len(a.abstract) > 200 else a.abstract,
            "journal": info["title"], "journal_slug": info["slug"],
            "volume": a.volume_number, "issue": a.issue_number,
            "first_page": a.first_page, "last_page": a.last_page,
            "publish_date": a.publish_date.strftime("%Y-%m-%d"),
            "url": f"/{a.publish_date.strftime('%Y')}/{a.publish_date.strftime('%m')}/{a.url}/",
            "pdf_url": f"/download-article/{a.file_path}",
            "issn": a.journal.split("|")[1] if "|" in a.journal else "",
        })
    return JSONResponse(result)


# =====================================================================
# SITEMAP
# =====================================================================
@app.get("/sitemap", response_class=Response)
@app.get("/sitemap/", response_class=Response)
@app.get("/sitemap.xml", response_class=Response)
async def sitemap(request: Request, db: Session = Depends(get_db)):
    host_base = str(request.base_url).rstrip("/")
    skip = ("/my-account", "/admin", "/submissions", "/view-journal", "/edit-article",
            "/new-article", "/editors", "/new-editor", "/reviewer", "/new-reviewer",
            "/issue", "/eduvid-content", "/new-eduvid", "/messages", "/query")
    static_urls = []
    today = datetime.now().strftime("%Y-%m-%d")
    for route in app.routes:
        if not hasattr(route, "methods") or "GET" not in route.methods:
            continue
        path = getattr(route, "path", "")
        if any(path.startswith(p) for p in skip):
            continue
        if "{" in path:
            continue
        static_urls.append({"loc": f"{host_base}{path}", "lastmod": today})
    dynamic_urls = []
    for p in db.query(Article).all():
        dynamic_urls.append({
            "loc": f"{host_base}/{p.publish_date.strftime('%Y')}/{p.publish_date.strftime('%m')}/{p.url}/",
            "lastmod": p.publish_date.strftime("%Y-%m-%d"),
        })
    resp = templates.TemplateResponse("public/sitemap_enhanced.xml", {
        "request": request, "static_urls": static_urls,
        "dynamic_urls": dynamic_urls, "host_base": host_base,
    }, media_type="application/xml")
    resp.headers["Cache-Control"] = "public, max-age=3600"
    return resp


# =====================================================================
# HOME / SEARCH
# =====================================================================
@app.get("/", response_class=HTMLResponse)
async def index(request: Request, s: str = None, db: Session = Depends(get_db)):
    if s is not None:
        results, seen = [], set()
        for kw in s.split():
            pat = f"%{kw}%"
            for a in db.query(Article).filter(or_(
                Article.title.ilike(pat), Article.keywords.ilike(pat),
                Article.abstract.ilike(pat), Article.author.ilike(pat),
                Article.journal.ilike(pat),
            )).all():
                if a.id not in seen:
                    results.append(a)
                    seen.add(a.id)
        return render("search.html", request, articles=results, search_keyword=s)
    articles = db.query(Article).order_by(Article.publish_date.desc()).limit(10).all()
    video = db.query(Eduvid).filter(Eduvid.title == "HOME_VIDEO").first()
    return render("index.html", request, articles=articles, video=video)

@app.get("/custom/{pagename}", response_class=HTMLResponse)
async def custom_page(pagename: str, request: Request):
    logging.debug(pagename)
    return render("custom/conference2.html", request)


# =====================================================================
# ARTICLE PAGES
# =====================================================================
@app.get("/{year:int}/{month:int}/{url}/", response_class=HTMLResponse)
async def article_slash(year: int, month: int, url: str, request: Request, db: Session = Depends(get_db)):
    art = db.query(Article).filter_by(url=unquote(url)).first()
    if not art:
        return render("404/index.html", request)
    info = get_journal_info(art.journal)
    return render(info["template_dir"] + "/archive/template.html", request,
                  article=art, journal_title=info["title"],
                  journal_abbrev=info["abbrev"], journal_slug=info["slug"])

@app.get("/{year:int}/{month:int}/{url}", response_class=HTMLResponse)
async def article_no_slash(year: int, month: int, url: str, request: Request, db: Session = Depends(get_db)):
    art = db.query(Article).filter_by(url=unquote(url)).first()
    if not art:
        return render("404/index.html", request)
    info = get_journal_info(art.journal)
    return render(info["template_dir"] + "/archive/template.html", request,
                  article=art, journal_title=info["title"],
                  journal_abbrev=info["abbrev"], journal_slug=info["slug"])


# =====================================================================
# QUERY PAGE
# =====================================================================
@app.get("/query/", response_class=HTMLResponse)
async def query_get(request: Request):
    return render("query.html", request)

@app.post("/query/")
async def query_post(request: Request):
    form = await request.form()
    text = form.get("textbox", "")
    ts = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
    with open(f"saved_text_{ts}.txt", "w", encoding="utf-8") as f:
        f.write(text)
    return RedirectResponse("/", status_code=303)


# =====================================================================
# STATIC PAGES
# =====================================================================
@app.get("/journals/", response_class=HTMLResponse)
async def journals(request: Request):
    return render("journals/index.html", request)

@app.get("/privacy-policy/", response_class=HTMLResponse)
async def privacy(request: Request):
    return render("privacy-policy/index.html", request)

@app.get("/about-us/", response_class=HTMLResponse)
async def about(request: Request):
    return render("about-us/index.html", request)

@app.get("/contact-us-2/", response_class=HTMLResponse)
async def contact(request: Request):
    return render("contact-us-2/index.html", request)

@app.get("/eduvid/", response_class=HTMLResponse)
async def eduvid_page(request: Request, db: Session = Depends(get_db)):
    data = db.query(Eduvid).filter(Eduvid.title != "HOME_VIDEO").order_by(Eduvid.publish_date.desc()).all()
    return render("eduvid/index.html", request, data=data)

@app.get("/submit-success", response_class=HTMLResponse)
async def submit_success(request: Request):
    return render("submit-success/index.html", request)

@app.get("/submit-failure", response_class=HTMLResponse)
async def submit_failure(request: Request):
    return render("submit-failure/index.html", request)

@app.get("/payment/", response_class=HTMLResponse)
async def payment(request: Request):
    return render("payments/index.html", request)


# =====================================================================
# MANUSCRIPT SUBMISSION
# =====================================================================
@app.get("/submit-manuscript/", response_class=HTMLResponse)
async def manuscript_get(request: Request):
    return render("submit-manuscript/index.html", request)

@app.post("/submit-manuscript/")
async def manuscript_post(request: Request, db: Session = Depends(get_db)):
    form = await request.form()
    upload = form.get("wpforms_15990_6")
    if not upload or not upload.filename:
        return RedirectResponse("/submit-failure", status_code=303)
    if (allowed_file(upload.filename) and form.get("wpforms[fields][5]", "") != ""
        and form.get("wpforms[fields][0]", "") != "" and form.get("wpforms[fields][4]", "") != ""
        and form.get("wpforms[fields][1]", "") != "" and form.get("wpforms[fields][3]", "") != ""):
        fn = await save_upload(upload, "submission")
        db.add(Submissions(title=form["wpforms[fields][5]"].strip(), author=form["wpforms[fields][0]"].strip(),
                           journal=form["wpforms[fields][4]"].lower(), author_email=form["wpforms[fields][1]"].strip(),
                           author_phone=form["wpforms[fields][3]"].strip(), submit_date=datetime.now(), file_path=fn))
        db.commit()
        return RedirectResponse("/submit-success", status_code=303)
    return RedirectResponse("/submit-failure", status_code=303)


# =====================================================================
# FILE DOWNLOADS
# =====================================================================
@app.get("/uploads/{id}")
async def download_file(id: int, request: Request, db: Session = Depends(get_db)):
    require_login(request, db)
    sub = db.query(Submissions).filter_by(id=id).first()
    return FileResponse(os.path.join(UPLOAD_FOLDER, "submission", sub.file_path))

@app.get("/download-resume/{filename}")
async def download_resume(filename: str):
    return FileResponse(os.path.join(UPLOAD_FOLDER, "editor_request", filename))

@app.get("/download-article/{filename}")
async def download_article(filename: str):
    return FileResponse(os.path.join(UPLOAD_FOLDER, "article", filename))

@app.get("/assets/{filename}")
async def download_assets(filename: str):
    return FileResponse(os.path.join(UPLOAD_FOLDER, "others", filename))

@app.get("/conference_assets/{filename}")
async def download_conference_assets(filename: str):
    return FileResponse(os.path.join(UPLOAD_FOLDER, "conference", filename))


# =====================================================================
# CONFERENCE
# =====================================================================
@app.get("/conference/", response_class=HTMLResponse)
async def conference_page(request: Request, db: Session = Depends(get_db)):
    return render("conferences/index.html", request, conference=db.query(Conference).filter_by(id=1).first())

@app.get("/conference/{path:path}", response_class=HTMLResponse)
async def conference_details(path: str, request: Request, db: Session = Depends(get_db)):
    return render("conferences/template.html", request, conference=db.query(Conference).filter_by(id=1).first())


# =====================================================================
# EDITORIAL / REVIEWER
# =====================================================================
@app.get("/editorial-team/", response_class=HTMLResponse)
async def editorial_team(request: Request, journal: str = None, db: Session = Depends(get_db)):
    if journal and "journal" in journal:
        j = journal.replace("-archive", "").replace("-indexing", "").replace("-issues", "").replace("-aims-scope", "").replace("-article", "").replace("scientific-research", "sarcouncil")
        eds = db.query(Editors).filter_by(journal=j).all()
    else:
        eds = db.query(Editors).all()
    return render("editorial-team/index.html", request, data=eds)

@app.get("/reviewer-panel/", response_class=HTMLResponse)
async def reviewer_panel(request: Request, journal: str = None, db: Session = Depends(get_db)):
    if journal and "journal" in journal:
        j = journal.replace("-archive", "").replace("-indexing", "").replace("-issues", "").replace("-aims-scope", "").replace("-article", "").replace("scientific-research", "sarcouncil")
        revs = db.query(Reviewer).filter_by(journal=j).all()
    else:
        revs = db.query(Reviewer).all()
    return render("reviewer-panel/index.html", request, data=revs)


# =====================================================================
# FOR EDITORS (join)
# =====================================================================
@app.get("/for-editors/", response_class=HTMLResponse)
async def for_editors_get(request: Request):
    return render("for-editors/index.html", request)

@app.post("/for-editors/")
async def for_editors_post(request: Request, db: Session = Depends(get_db)):
    form = await request.form()
    upload = form.get("wpforms_15963_7")
    if not upload or not upload.filename:
        return RedirectResponse("/submit-failure", status_code=303)
    journals_list = form.getlist("wpforms[fields][8][]")
    if (allowed_file(upload.filename) and form.get("wpforms[fields][0]", "") != ""
        and form.get("wpforms[fields][2]", "") != "" and form.get("wpforms[fields][3]", "") != ""
        and len(journals_list) > 0):
        fn = await save_upload(upload, "editor_request")
        db.add(EditorRequest(name=form["wpforms[fields][0]"].strip(), email=form["wpforms[fields][3]"].strip(),
                             phone=form["wpforms[fields][2]"].strip(), journals=",".join(journals_list),
                             filepath=fn, submit_date=datetime.now()))
        db.commit()
        return RedirectResponse("/submit-success", status_code=303)
    return RedirectResponse("/submit-failure", status_code=303)


# =====================================================================
# AUTH
# =====================================================================
@app.get("/my-account/", response_class=HTMLResponse)
async def my_account_get(request: Request, db: Session = Depends(get_db)):
    if get_current_user(request, db):
        return RedirectResponse("/admin-panel", status_code=303)
    return render("my-account/index.html", request)

@app.post("/my-account/")
async def my_account_post(request: Request, db: Session = Depends(get_db)):
    form = await request.form()
    user = db.query(Login).filter_by(username=form.get("username", "")).first()
    if user and check_password_hash(user.password, form.get("password", "")):
        request.session["user_id"] = user.id
        return RedirectResponse("/admin-panel", status_code=303)
    return render("my-account/index.html", request, login_error="Invalid username or password. Please try again.")

@app.get("/logout")
async def logout(request: Request):
    request.session.clear()
    return RedirectResponse("/", status_code=303)

@app.post("/change_password")
async def change_password(request: Request, db: Session = Depends(get_db)):
    user = require_login(request, db)
    form = await request.form()
    new_pw = form.get("new_password", "").strip()
    if not check_password_hash(user.password, form.get("current_password", "")):
        return render("admin-panel/settings.html", request, active_page="settings",
                      flash_error="Current password is incorrect.")
    if len(new_pw) < 4:
        return render("admin-panel/settings.html", request, active_page="settings",
                      flash_error="New password must be at least 4 characters.")
    user.password = generate_password_hash(new_pw)
    db.commit()
    return render("admin-panel/settings.html", request, active_page="settings",
                  flash_success="Password changed successfully.")

@app.get("/forgot/", response_class=HTMLResponse)
async def forgot_get(request: Request):
    return render("my-account/forgot.html", request)

@app.post("/forgot/")
async def forgot_post(request: Request, db: Session = Depends(get_db)):
    return RedirectResponse("/my-account/", status_code=303)

@app.get("/resetpass/{token}", response_class=HTMLResponse)
async def resetpass_get(token: str, request: Request):
    return render("my-account/reset.html", request)

@app.post("/resetpass/{token}")
async def resetpass_post(token: str, request: Request, db: Session = Depends(get_db)):
    return RedirectResponse("/my-account/", status_code=303)


# =====================================================================
# ADMIN PANEL
# =====================================================================
@app.get("/admin-panel", response_class=HTMLResponse)
async def admin_panel(request: Request, db: Session = Depends(get_db)):
    require_login(request, db)
    return render("admin-panel/view.html", request)

@app.get("/submissions", response_class=HTMLResponse)
async def submissions_page(request: Request, db: Session = Depends(get_db)):
    require_login(request, db)
    return render("admin-panel/submission.html", request, submissions=db.query(Submissions).all())

@app.get("/settings/", response_class=HTMLResponse)
async def settings_get(request: Request, db: Session = Depends(get_db)):
    require_login(request, db)
    return render("admin-panel/settings.html", request)

@app.post("/settings/")
async def settings_post(request: Request, db: Session = Depends(get_db)):
    require_login(request, db)
    form = await request.form()
    upload = form.get("uploadFile")
    if upload and upload.filename and allowed_video(upload.filename):
        fn = await save_upload(upload, "others")
        db.add(Eduvid(title="HOME_VIDEO", media=fn, content="", publish_date=datetime.now()))
        db.commit()
    return render("admin-panel/settings.html", request)

@app.get("/conference_settings/", response_class=HTMLResponse)
async def conference_settings(request: Request, db: Session = Depends(get_db)):
    require_login(request, db)
    return render("admin-panel/conference_settings.html", request, conference=db.query(Conference).first())

@app.get("/edit-conference/", response_class=HTMLResponse)
async def conference_edit_get(request: Request, db: Session = Depends(get_db)):
    require_login(request, db)
    conf = db.query(Conference).filter_by(id=1).first()
    if not conf:
        conf = {"title": "", "announcement": "", "conference_details": ""}
    return render("admin-panel/edit_conference.html", request, conference=conf)

@app.post("/edit-conference/")
async def conference_edit_post(request: Request, db: Session = Depends(get_db)):
    require_login(request, db)
    form = await request.form()
    fb = form.get("uploadFile")
    fp = form.get("uploadPicture")
    if (fb and fb.filename and fp and fp.filename and allowed_file(fb.filename) and allowed_image(fp.filename)
        and form.get("conferenceTitle", "") and form.get("announcement", "") and form.get("conference", "")):
        fn_b = await save_upload(fb, "conference")
        fn_p = await save_upload(fp, "conference")
        conf = db.query(Conference).filter_by(id=1).first()
        conf.title = form["conferenceTitle"].strip()
        conf.announcement = form["announcement"].strip()
        conf.conference_details = form["conference"].strip()
        conf.poster = fn_p
        conf.brochure = fn_b
        conf.submit_date = datetime.now()
        db.commit()
        return RedirectResponse("/conference_settings/", status_code=303)
    return RedirectResponse("/submit-failure", status_code=303)

@app.get("/editorrequest", response_class=HTMLResponse)
async def editorrequest(request: Request, db: Session = Depends(get_db)):
    require_login(request, db)
    return render("admin-panel/editor_request.html", request,
                  editorrequest=db.query(EditorRequest).order_by(EditorRequest.submit_date.desc()).all())

@app.get("/view-journal", response_class=HTMLResponse)
async def view_journals(request: Request, journal: str = "", db: Session = Depends(get_db)):
    require_login(request, db)
    jk = journal.split("|")[0].replace("-", " ") + "|" + journal.split("|")[1]
    articles = db.query(Article).filter_by(journal=jk).all()
    data = {}
    for a in articles:
        if a.volume_year not in data:
            data[a.volume_year] = []
        obj = {"issue_number": a.issue_number, "volume_number": a.volume_number, "issue_identifier": f"issue-{a.issue_number}"}
        if obj not in data[a.volume_year]:
            data[a.volume_year].append(obj)
    return render("admin-panel/journal.html", request, data=data, journal=journal)

@app.get("/issue", response_class=HTMLResponse)
async def issue_page(request: Request, journal: str = "", issue_number: str = "", volume_number: str = "", volume_year: str = "", db: Session = Depends(get_db)):
    require_login(request, db)
    jk = journal.split("|")[0].replace("-", " ") + "|" + journal.split("|")[1]
    data = db.query(Article).filter_by(journal=jk, issue_number=issue_number, volume_number=volume_number).all()
    return render("admin-panel/issue.html", request, data=data, journal=journal,
                  issue_number=issue_number, volume_number=volume_number, volume_year=volume_year)


# =====================================================================
# ADMIN: ARTICLES CRUD
# =====================================================================
@app.get("/edit-article", response_class=HTMLResponse)
async def edit_article_get(request: Request, id: int = 0, db: Session = Depends(get_db)):
    require_login(request, db)
    return render("admin-panel/edit_article.html", request, article=db.query(Article).filter_by(id=id).first())

@app.post("/edit-article")
async def edit_article_post(request: Request, db: Session = Depends(get_db)):
    require_login(request, db)
    form = await request.form()
    upload = form.get("uploadFile")
    fn = None
    if upload and upload.filename:
        if allowed_file(upload.filename) and len(form.get("volumeYear", "")) == 4:
            fn = await save_upload(upload, "article")
        else:
            return RedirectResponse("/submit-failure", status_code=303)
    if (form.get("firstPage") and form.get("lastPage") and len(form.get("volumeYear", "")) == 4
        and form.get("articleTitle") and form.get("volumeNumber") and form.get("issueNumber")
        and form.get("authorName") and form.get("keywords") and form.get("abstract")):
        art = db.query(Article).filter_by(id=int(form["articleID"])).first()
        art.journal = form["selectJournal"].lower()
        art.volume_number = form["volumeNumber"]
        art.publish_date = datetime.strptime(form["publishDate"], "%Y-%m-%d")
        art.volume_year = form["volumeYear"]
        art.issue_number = form["issueNumber"]
        art.title = form["articleTitle"]
        art.author = form["authorName"]
        art.keywords = form["keywords"]
        art.abstract = form["abstract"]
        art.first_page = form["firstPage"]
        art.last_page = form["lastPage"]
        art.url = slugify(form.get("articleURL", ""), form["articleTitle"])
        if fn:
            art.file_path = fn
        db.commit()
        return RedirectResponse("/submit-success", status_code=303)
    return RedirectResponse("/submit-failure", status_code=303)

@app.get("/new-article", response_class=HTMLResponse)
async def new_article_get(request: Request, journal: str = "", db: Session = Depends(get_db)):
    require_login(request, db)
    return render("admin-panel/new_article.html", request, journal=journal)

@app.post("/new-article")
async def new_article_post(request: Request, db: Session = Depends(get_db)):
    require_login(request, db)
    form = await request.form()
    upload = form.get("uploadFile")
    if not upload or not upload.filename:
        return RedirectResponse("/submit-failure", status_code=303)
    if (allowed_file(upload.filename) and form.get("firstPage") and form.get("lastPage")
        and len(form.get("volumeYear", "")) == 4 and form.get("articleTitle")
        and form.get("volumeNumber") and form.get("issueNumber")
        and form.get("authorName") and form.get("keywords") and form.get("abstract")):
        fn = await save_upload(upload, "article")
        db.add(Article(journal=form["selectJournal"].lower(), volume_number=form["volumeNumber"].strip(),
                       volume_year=form["volumeYear"].strip(), issue_number=form["issueNumber"].strip(),
                       title=form["articleTitle"].strip(), author=form["authorName"].strip(),
                       keywords=form["keywords"].strip(), abstract=form["abstract"].strip(),
                       first_page=form["firstPage"].strip(), last_page=form["lastPage"].strip(),
                       file_path=fn, publish_date=datetime.strptime(form["publishDate"], "%Y-%m-%d"),
                       url=slugify(form.get("articleURL", ""), form["articleTitle"])))
        db.commit()
        return RedirectResponse("/submit-success", status_code=303)
    return RedirectResponse("/submit-failure", status_code=303)

@app.get("/delete-article/{id}")
async def delete_article(id: int, request: Request, db: Session = Depends(get_db)):
    require_login(request, db)
    art = db.query(Article).filter_by(id=id).first()
    fp = os.path.join(UPLOAD_FOLDER, "article", art.file_path)
    if os.path.exists(fp):
        os.remove(fp)
    db.delete(art)
    db.commit()
    return RedirectResponse("/admin-panel", status_code=303)

@app.get("/delete-request/{phone}")
async def delete_request(phone: str, db: Session = Depends(get_db)):
    er = db.query(EditorRequest).filter_by(phone=phone).first()
    fp = os.path.join(UPLOAD_FOLDER, "editor_request", er.filepath)
    if os.path.exists(fp):
        os.remove(fp)
    db.delete(er)
    db.commit()
    return RedirectResponse("/admin-panel", status_code=303)

@app.get("/delete-submission/{id}")
async def delete_submission(id: int, db: Session = Depends(get_db)):
    db.delete(db.query(Submissions).filter_by(id=id).first())
    db.commit()
    return RedirectResponse("/submissions", status_code=303)


# =====================================================================
# ADMIN: EDITORS CRUD
# =====================================================================
@app.get("/editors", response_class=HTMLResponse)
async def editors_page(request: Request, db: Session = Depends(get_db)):
    require_login(request, db)
    return render("admin-panel/editors.html", request, data=db.query(Editors).all())

@app.get("/new-editor", response_class=HTMLResponse)
async def editor_new_get(request: Request, db: Session = Depends(get_db)):
    require_login(request, db)
    return render("admin-panel/new_editor.html", request)

@app.post("/new-editor")
async def editor_new_post(request: Request, db: Session = Depends(get_db)):
    require_login(request, db)
    form = await request.form()
    if form.get("editorName") and form.get("editorDesignation") and form.get("description"):
        upload = form.get("uploadFile")
        img = ""
        if upload and upload.filename and allowed_image(upload.filename):
            img = await save_upload(upload, "others")
        db.add(Editors(name=form["editorName"].strip(), designation=form["editorDesignation"].strip(),
                       journal=form.get("selectJournal", "").strip(), image=img, description=form["description"].strip()))
        db.commit()
        return RedirectResponse("/editors", status_code=303)
    return RedirectResponse("/submit-failure", status_code=303)

@app.get("/edit-editor/{id}", response_class=HTMLResponse)
async def editor_edit_get(id: int, request: Request, db: Session = Depends(get_db)):
    require_login(request, db)
    return render("admin-panel/edit_editor.html", request, editor=db.query(Editors).filter_by(id=id).first())

@app.post("/edit-editor/{id}")
async def editor_edit_post(id: int, request: Request, db: Session = Depends(get_db)):
    require_login(request, db)
    form = await request.form()
    if form.get("editorName") and form.get("editorDesignation") and form.get("description"):
        ed = db.query(Editors).filter_by(id=id).first()
        ed.name = form["editorName"].strip()
        upload = form.get("uploadFile")
        if upload and upload.filename and allowed_image(upload.filename):
            ed.image = await save_upload(upload, "others")
        ed.journal = form.get("selectJournal", "").strip()
        ed.designation = form["editorDesignation"].strip()
        ed.description = form["description"].strip()
        db.commit()
        return RedirectResponse("/editors", status_code=303)
    return RedirectResponse("/submit-failure", status_code=303)

@app.get("/delete-editor/{id}")
async def editor_delete(id: int, request: Request, db: Session = Depends(get_db)):
    require_login(request, db)
    db.delete(db.query(Editors).filter_by(id=id).first())
    db.commit()
    return RedirectResponse("/editors", status_code=303)


# =====================================================================
# ADMIN: REVIEWERS CRUD
# =====================================================================
@app.get("/reviewer", response_class=HTMLResponse)
async def reviewer_page(request: Request, db: Session = Depends(get_db)):
    require_login(request, db)
    return render("admin-panel/reviewers.html", request, data=db.query(Reviewer).all())

@app.get("/new-reviewer", response_class=HTMLResponse)
async def reviewer_new_get(request: Request, db: Session = Depends(get_db)):
    require_login(request, db)
    return render("admin-panel/new_reviewer.html", request)

@app.post("/new-reviewer")
async def reviewer_new_post(request: Request, db: Session = Depends(get_db)):
    require_login(request, db)
    form = await request.form()
    if form.get("reviewerName") and form.get("description"):
        upload = form.get("uploadFile")
        img = ""
        if upload and upload.filename and allowed_image(upload.filename):
            img = await save_upload(upload, "others")
        db.add(Reviewer(name=form["reviewerName"].strip(), journal=form.get("selectJournal", "").strip(),
                        image=img, description=form["description"].strip()))
        db.commit()
        return RedirectResponse("/reviewer", status_code=303)
    return RedirectResponse("/submit-failure", status_code=303)

@app.get("/edit-reviewer/{id}", response_class=HTMLResponse)
async def reviewer_edit_get(id: int, request: Request, db: Session = Depends(get_db)):
    require_login(request, db)
    return render("admin-panel/edit_reviewer.html", request, reviewer=db.query(Reviewer).filter_by(id=id).first())

@app.post("/edit-reviewer/{id}")
async def reviewer_edit_post(id: int, request: Request, db: Session = Depends(get_db)):
    require_login(request, db)
    form = await request.form()
    if form.get("reviewerName") and form.get("description"):
        rev = db.query(Reviewer).filter_by(id=id).first()
        rev.name = form["reviewerName"].strip()
        rev.journal = form.get("selectJournal", "").strip()
        rev.description = form["description"].strip()
        upload = form.get("uploadFile")
        if upload and upload.filename and allowed_image(upload.filename):
            rev.image = await save_upload(upload, "others")
        db.commit()
        return RedirectResponse("/reviewer", status_code=303)
    return RedirectResponse("/submit-failure", status_code=303)

@app.get("/delete-reviewer/{id}")
async def reviewer_delete(id: int, request: Request, db: Session = Depends(get_db)):
    require_login(request, db)
    db.delete(db.query(Reviewer).filter_by(id=id).first())
    db.commit()
    return RedirectResponse("/reviewer", status_code=303)


# =====================================================================
# ADMIN: EDUVID CRUD
# =====================================================================
@app.get("/eduvid-content", response_class=HTMLResponse)
async def eduvid_content(request: Request, db: Session = Depends(get_db)):
    require_login(request, db)
    return render("admin-panel/eduvid_content.html", request,
                  data=db.query(Eduvid).filter(Eduvid.title != "HOME_VIDEO").order_by(Eduvid.publish_date.desc()).all())

@app.get("/new-eduvid", response_class=HTMLResponse)
async def eduvid_new_get(request: Request, db: Session = Depends(get_db)):
    require_login(request, db)
    return render("admin-panel/new_eduvid.html", request)

@app.post("/new-eduvid")
async def eduvid_new_post(request: Request, db: Session = Depends(get_db)):
    require_login(request, db)
    form = await request.form()
    if form.get("eduvidTitle") and form.get("content"):
        upload = form.get("uploadFile")
        if upload and upload.filename and (allowed_video(upload.filename) or allowed_image(upload.filename)):
            fn = await save_upload(upload, "others")
            db.add(Eduvid(title=form["eduvidTitle"].strip(), media="/assets/" + fn,
                          content=form["content"].strip(), publish_date=datetime.now()))
            db.commit()
            return RedirectResponse("/eduvid-content", status_code=303)
        elif form.get("mediaURL", ""):
            db.add(Eduvid(title=form["eduvidTitle"].strip(), media=form["mediaURL"].strip(),
                          content=form["content"].strip(), publish_date=datetime.now()))
            db.commit()
            return RedirectResponse("/eduvid-content", status_code=303)
    return RedirectResponse("/submit-failure", status_code=303)

@app.get("/edit-eduvid/{id}", response_class=HTMLResponse)
async def eduvid_edit_get(id: int, request: Request, db: Session = Depends(get_db)):
    require_login(request, db)
    return render("admin-panel/edit_eduvid.html", request,
                  eduvid=db.query(Eduvid).filter(Eduvid.title != "HOME_VIDEO").filter_by(id=id).first())

@app.post("/edit-eduvid/{id}")
async def eduvid_edit_post(id: int, request: Request, db: Session = Depends(get_db)):
    require_login(request, db)
    form = await request.form()
    if form.get("eduvidTitle") and form.get("content"):
        ev = db.query(Eduvid).filter(Eduvid.title != "HOME_VIDEO").filter_by(id=id).first()
        ev.title = form["eduvidTitle"].strip()
        ev.media = form.get("mediaURL", "").strip()
        ev.content = form["content"].strip()
        upload = form.get("uploadFile")
        if upload and upload.filename and (allowed_video(upload.filename) or allowed_image(upload.filename)):
            fn = await save_upload(upload, "others")
            ev.media = "/assets/" + fn
        db.commit()
        return RedirectResponse("/eduvid-content", status_code=303)
    return RedirectResponse("/submit-failure", status_code=303)

@app.get("/delete-eduvid/{id}")
async def eduvid_delete(id: int, request: Request, db: Session = Depends(get_db)):
    require_login(request, db)
    ev = db.query(Eduvid).filter(Eduvid.title != "HOME_VIDEO").filter_by(id=id).first()
    fp = os.path.join(UPLOAD_FOLDER, "others", ev.media.replace("/assets/", ""))
    if os.path.exists(fp):
        os.remove(fp)
    db.delete(ev)
    db.commit()
    return RedirectResponse("/eduvid-content", status_code=303)


# =====================================================================
# ADMIN: MESSAGES
# =====================================================================
@app.get("/messages", response_class=HTMLResponse)
async def messages_page(request: Request, db: Session = Depends(get_db)):
    require_login(request, db)
    return render("admin-panel/messages.html", request, data=db.query(Messages).order_by(Messages.submit_date.desc()).all())

@app.get("/delete-message/{id}")
async def message_delete(id: int, request: Request, db: Session = Depends(get_db)):
    require_login(request, db)
    db.delete(db.query(Messages).filter_by(id=id).first())
    db.commit()
    return RedirectResponse("/messages", status_code=303)

@app.post("/submit-message")
async def submit_messages(request: Request, db: Session = Depends(get_db)):
    form = await request.form()
    if (form.get("wpforms[fields][1]", "").strip() and form.get("wpforms[fields][0]", "").strip()
        and form.get("wpforms[fields][3]", "").strip() and form.get("wpforms[fields][2]", "").strip()):
        db.add(Messages(name=form["wpforms[fields][0]"].strip(), email=form["wpforms[fields][1]"].strip(),
                        phone=form["wpforms[fields][3]"].strip(), message=form["wpforms[fields][2]"].strip(),
                        submit_date=datetime.now()))
        db.commit()
        return RedirectResponse("/", status_code=303)
    return RedirectResponse("/submit-failure", status_code=303)


# =====================================================================
# JOURNAL-SPECIFIC ROUTES (20 journals)
# =====================================================================
def _build_archive_data(articles):
    data = {}
    for a in articles:
        if a.volume_year not in data:
            data[a.volume_year] = []
        obj = {"issue_number": a.issue_number, "volume_number": a.volume_number, "issue_identifier": f"issue-{a.issue_number}"}
        if obj not in data[a.volume_year]:
            data[a.volume_year].append(obj)
    return data


def register_journal_routes(slug, journal_key, template_dir, editorial_slug, editorial_journal):
    jn = journal_key.split("|")[0]
    jissn = journal_key.split("|")[1] if "|" in journal_key else ""
    jinfo = JOURNAL_MAP.get(journal_key.lower(), {})
    jtitle = jinfo.get("title", jn.title())

    @app.get(f"/{slug}/", response_class=HTMLResponse)
    async def _home(request: Request, db: Session = Depends(get_db), _jk=journal_key, _td=template_dir, _jt=jtitle, _ji=jissn, _sl=slug):
        return render(f"{_td}/index.html", request,
                      articles=db.query(Article).filter_by(journal=_jk).order_by(Article.publish_date.desc()).limit(10).all(),
                      journal_title=_jt, journal_issn=_ji, journal_url=f"https://sarcouncil.com/{_sl}/")

    @app.get(f"/{slug}-aims-scope/", response_class=HTMLResponse)
    async def _aims(request: Request, _td=template_dir):
        return render(f"{_td}/aims-scope/index.html", request)

    @app.get(f"/{slug}-archive/", response_class=HTMLResponse)
    async def _archive(request: Request, db: Session = Depends(get_db), _jk=journal_key, _td=template_dir):
        return render(f"{_td}/archive/index.html", request,
                      data=_build_archive_data(db.query(Article).filter_by(journal=_jk).order_by(Article.publish_date.desc()).all()))

    @app.get(f"/{slug}-issues/", response_class=HTMLResponse)
    async def _issues(request: Request, issue_number: str = "", volume_number: str = "", volume_year: str = "",
                      db: Session = Depends(get_db), _jk=journal_key, _td=template_dir, _jn=jn):
        return render(f"{_td}/archive/issue.html", request,
                      data=db.query(Article).filter_by(journal=_jk, issue_number=issue_number, volume_number=volume_number).order_by(Article.publish_date.desc()).all(),
                      journal=_jn, issue_number=issue_number, volume_number=volume_number, volume_year=volume_year)

    @app.get(f"/{slug}-article/{{id}}", response_class=HTMLResponse)
    async def _article(id: str, request: Request, db: Session = Depends(get_db)):
        data = db.query(Article).filter_by(url=id).first()
        info = get_journal_info(data.journal)
        return render(info["template_dir"] + "/archive/template.html", request,
                      article=data, journal_title=info["title"], journal_abbrev=info["abbrev"], journal_slug=info["slug"])

    @app.get(f"/{slug}-indexing/", response_class=HTMLResponse)
    async def _indexing(request: Request, _td=template_dir):
        return render(f"{_td}/indexing/index.html", request)

    @app.get(f"/{editorial_slug}/", response_class=HTMLResponse)
    async def _editorial(request: Request, db: Session = Depends(get_db), _ej=editorial_journal):
        return render("editorial-team/index.html", request, data=db.query(Editors).filter_by(journal=_ej).all())


register_journal_routes("scientific-research-journal-of-applied-sciences", "journal of applied sciences|2945-3437", "sarcouncil-journal-of-applied-sciences", "editorial-board-applied-sciences", "sarcouncil-journal-of-applied-sciences")
register_journal_routes("sarcouncil-journal-arts-literature", "journal of arts and literature|2945-364X", "sarcouncil-journal-of-arts-and-literature", "editorial-responsibility-of-arts-and-literature", "sarcouncil-journal-of-arts-and-literature")
register_journal_routes("sarcouncil-journal-of-agriculture", "journal of agriculture|2945-3631", "sarcouncil-journal-of-agriculture", "editorial-responsibility-agriculture", "sarcouncil-journal-of-agriculture")
register_journal_routes("scientific-research-journal-of-arts-humanities-and-social-sciences", "journal of arts humanities and social sciences|2945-3488", "sarcouncil-journal-of-arts-humanities-and-social-sciences", "editorial-board-of-arts-humanities-and-social-sciences", "sarcouncil-journal-of-arts-humanities-and-social-sciences")
register_journal_routes("scientific-journal-of-biomedical-sciences", "journal of biomedical sciences|2945-3666", "sarcouncil-journal-of-biomedical-sciences", "editorial-responsibility-of-biomedical-sciences", "sarcouncil-journal-of-biomedical-sciences")
register_journal_routes("scientific-research-journal-of-economics-and-business-management", "journal of economics and business management|2945-3593", "sarcouncil-journal-of-economics-and-business-management", "editorial-board-of-economics-and-business-management", "sarcouncil-journal-of-economics-and-business-management")
register_journal_routes("sarcouncil-journal-of-education-and-sociology", "journal of education and sociology|2945-3542", "sarcouncil-journal-of-education-and-sociology", "editorial-board-education-and-sociology", "sarcouncil-journal-of-education-and-sociology")
register_journal_routes("scientific-research-journal-of-engineering-and-computer-sciences", "journal of engineering and computer sciences|2945-3585", "sarcouncil-journal-of-engineering-and-computer-sciences", "editorial-board-of-computer-sciences", "sarcouncil-journal-of-engineering-and-computer-sciences")
register_journal_routes("scientific-research-journal-entrepreneurship-and-business-management", "journal of entrepreneurship and business management|2945-3720", "sarcouncil-journal-of-entrepreneurship-and-business-management", "editorial-board-of-entrepreneurship-and-business-management", "sarcouncil-journal-of-entrepreneurship-and-business-management")
register_journal_routes("sarcouncil-journal-humanities-cultural-studies", "journal of humanities and cultural studies|2945-3658", "sarcouncil-journal-of-humanities-studies", "editorial-responsibility-of-humanities-and-cultural-studies", "sarcouncil-journal-of-humanities-studies")
register_journal_routes("scientific-journal-of-internal-medicine-public-health", "journal of internal medicine and public health|2945-3674", "sarcouncil-journal-of-internal-medicine-and-public-health", "editorial-responsibility-of-internal-medicine-and-public-health", "sarcouncil-journal-of-internal-medicine-and-public-health")
register_journal_routes("scientific-research-journal-of-medical-sciences-2", "journal of medical sciences|2945-3526", "sarcouncil-journal-of-medical-sciences", "editorial-board-medical-sciences", "sarcouncil-journal-of-medical-sciences")
register_journal_routes("scientific-research-journal-medical-series", "journal of medical series|2945-3550", "sarcouncil-journal-of-medical-series", "editorial-board-of-medical-series", "sarcouncil-journal-of-medical-series")
register_journal_routes("scientific-research-journal-of-medicine-surgery", "journal of medicine and surgery|2945-3534", "sarcouncil-journal-of-medicine-and-surgery", "editorial-board-of-medicine-surgery", "sarcouncil-journal-of-medicine-and-surgery")
register_journal_routes("scientific-research-journal-of-multidisciplinary-2", "journal of multidisciplinary|2945-3445", "sarcouncil-journal-of-multidisciplinary", "editorial-board-of-multidisciplinary", "sarcouncil-journal-of-multidisciplinary")
register_journal_routes("sarcouncil-journal-plant-agronomy", "journal of plant and agronomy|2980-4981", "sarcouncil-journal-of-plant-and-agronomy", "editorial-responsibility-plant-agronomy", "sarcouncil-journal-of-plant-and-agronomy")
register_journal_routes("sarcouncil-journal-of-public-administration-management", "journal of public administration and management|2945-3712", "sarcouncil-journal-of-public-administration-and-management", "editorial-responsibility-of-public-administration-and-management", "sarcouncil-journal-of-public-administration-and-management")
register_journal_routes("journal-of-economics-intelligence-and-technology", "journal of economics intelligence and technology|3082-3994", "journal-of-economics-intelligence-and-technology", "editorial-board-economics-intelligence-technology", "journal-of-economics-intelligence-and-technology")
register_journal_routes("journal-of-innovative-science", "journal of innovative science|3082-4435", "journal-of-innovative-science", "Editorial-Board-Journal-Innovative-Science", "journal-of-innovative-science")
register_journal_routes("Technology-Perception", "technology perception|3082-4451", "technology-perception", "editorial-responsibility-technology-perception", "technology-perception")


# =====================================================================
# ERROR HANDLERS
# =====================================================================
from starlette.exceptions import HTTPException as StarletteHTTPException

@app.exception_handler(404)
async def not_found_handler(request: Request, exc):
    return render("404/index.html", request)

@app.exception_handler(StarletteHTTPException)
async def http_exception_handler(request: Request, exc: StarletteHTTPException):
    if exc.status_code == 303:
        return RedirectResponse(exc.headers.get("Location", "/"), status_code=303)
    if exc.status_code == 404:
        return render("404/index.html", request)
    return HTMLResponse(f"Error {exc.status_code}", status_code=exc.status_code)


# =====================================================================
# ENTRYPOINT
# =====================================================================
if __name__ == "__main__":
    import uvicorn
    for d in ["uploads/article", "uploads/submission", "uploads/others",
              "uploads/editor_request", "uploads/conference"]:
        os.makedirs(d, exist_ok=True)
    uvicorn.run("app:app", host="127.0.0.1", port=5000, reload=True)
